blob: f65cad45410f82c1cd8af376a7b3fade902ffcbc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.core.tests.runtime.jobs;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.core.runtime.*;
import org.eclipse.core.runtime.jobs.Job;
import org.junit.FixMethodOrder;
import org.junit.Test;
import org.junit.runners.MethodSorters;
/**
* Test for bug 574883
*/
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class Bug_574883 extends AbstractJobManagerTest {
static class SerialExecutor extends Job {
private final List<Runnable> queue;
private final Object myFamily;
/**
* @param jobName descriptive job name
* @param family non null object to control this job execution
**/
public SerialExecutor(String jobName, Object family) {
super(jobName);
Assert.isNotNull(family);
this.myFamily = family;
this.queue = Collections.synchronizedList(new LinkedList<>());
setSystem(true);
}
@Override
public boolean belongsTo(Object family) {
return myFamily == family;
}
@Override
protected IStatus run(IProgressMonitor monitor) {
Runnable action = queue.remove(0);
try {
if (action != null && !monitor.isCanceled()) {
action.run();
}
} finally {
if (!queue.isEmpty() && !monitor.isCanceled()) {
// this call confuses JobManager and causes bug 574883 if the action above
// runs *too fast*
schedule();
}
}
return Status.OK_STATUS;
}
/**
* Enqueue an action asynchronously.
*/
public void schedule(Runnable action) {
queue.add(action);
schedule();
}
}
final int RUNS = 100_000;
final int processors = Runtime.getRuntime().availableProcessors();
@Test
public void testReschedulingLambda() throws InterruptedException {
// Executor has to execute every task. Even when they are scheduled fast
// and execute fast
SerialExecutor serialExecutor = new SerialExecutor("test", this);
AtomicInteger executions = new AtomicInteger();
for (int i = 0; i < RUNS; i++) {
serialExecutor.schedule(() -> executions.incrementAndGet());
}
Job.getJobManager().join(this, null);
Job[] jobs = Job.getJobManager().find(this);
int length = jobs.length;
int firstState = executions.get();
try {
if (length > 0) {
// Check if that still would work?
Job.getJobManager().join(this, null);
if (Job.getJobManager().find(this).length > 0) {
fail("Job still running after second join, executed before: " + firstState + ", executed now: "
+ executions.get() + ", cpu: " + processors);
}
}
assertEquals("Job still running after first join, executed: " + firstState + ", cpu: " + processors, 0,
length);
assertEquals(RUNS, executions.get());
} catch (Throwable t) {
// TODO: print the fail only, as long as bug 574883 is not fixed yet
t.printStackTrace(System.out);
t.printStackTrace(System.err);
Job.getJobManager().cancel(this);
Thread.sleep(1000);
Job.getJobManager().join(this, null);
}
}
@Test
public void testReschedulingMethodRef() throws InterruptedException {
// Executor has to execute every task. Even when they are scheduled fast
// and execute fast
SerialExecutor serialExecutor = new SerialExecutor("test", this);
AtomicInteger executions = new AtomicInteger();
for (int i = 0; i < RUNS; i++) {
serialExecutor.schedule(executions::incrementAndGet);
}
Job.getJobManager().join(this, null);
Job[] jobs = Job.getJobManager().find(this);
int length = jobs.length;
int firstState = executions.get();
try {
if (length > 0) {
// Check if that still would work?
Job.getJobManager().join(this, null);
if (Job.getJobManager().find(this).length > 0) {
fail("Job still running after second join, executed before: " + firstState + ", executed now: "
+ executions.get() + ", cpu: " + processors);
}
}
assertEquals("Job still running after first join, executed: " + firstState + ", cpu: " + processors, 0,
length);
assertEquals(RUNS, executions.get());
} catch (Throwable t) {
// TODO: print the fail only, as long as bug 574883 is not fixed yet
t.printStackTrace(System.out);
t.printStackTrace(System.err);
Job.getJobManager().cancel(this);
Thread.sleep(1000);
Job.getJobManager().join(this, null);
}
}
/**
* This test always passes because it does a bit more work as both tests above
* inside run method
*/
@Test
public void testReschedulingSomeMoreWork() throws InterruptedException {
// Executor has to execute every task. Even when they are scheduled fast
// and execute fast
SerialExecutor serialExecutor = new SerialExecutor("test", this);
AtomicInteger executions = new AtomicInteger();
AtomicLong garbage = new AtomicLong(42);
for (int i = 0; i < RUNS; i++) {
serialExecutor.schedule(() -> {
executions.incrementAndGet();
// just consume some more CPU cycles
garbage.getAndUpdate(x -> (long) (x + Math.sin(x) * 100));
});
}
Job.getJobManager().join(this, null);
Job[] jobs = Job.getJobManager().find(this);
int length = jobs.length;
int firstState = executions.get();
System.out.println(garbage);
try {
if (length > 0) {
// Check if that still would work?
Job.getJobManager().join(this, null);
if (Job.getJobManager().find(this).length > 0) {
fail("Job still running after second join, executed before: " + firstState + ", executed now: "
+ executions.get() + ", cpu: " + processors);
}
}
assertEquals("Job still running after first join, executed: " + firstState + ", cpu: " + processors, 0,
length);
assertEquals(RUNS, executions.get());
} catch (Throwable t) {
// TODO: print the fail only, as long as bug 574883 is not fixed yet
t.printStackTrace(System.out);
t.printStackTrace(System.err);
Job.getJobManager().cancel(this);
Thread.sleep(1000);
Job.getJobManager().join(this, null);
}
}
@Test
public void testNow() throws Exception {
AtomicInteger executions = new AtomicInteger();
for (int i = 0; i < RUNS; i++) {
long t1 = now();
((Runnable) () -> executions.incrementAndGet()).run();
long t2 = now();
long diff = t2 - t1;
assertTrue("Time should not go back: " + diff + " at: " + i, diff >= 0);
}
assertEquals(RUNS, executions.get());
}
}