use of org.eclipse.scout.rt.platform.job.IFuture in project scout.rt by eclipse.
the class JobListenerBlockedFutureTest method testEventsForBlockingJob.
@Test(timeout = 10000)
public void testEventsForBlockingJob() {
final IBlockingCondition condition = Jobs.newBlockingCondition(true);
IClientSession clientSession = mock(IClientSession.class);
when(clientSession.getModelJobSemaphore()).thenReturn(Jobs.newExecutionSemaphore(1));
JobEventCaptureListener captureListener = new JobEventCaptureListener();
Jobs.getJobManager().addListener(ModelJobs.newEventFilterBuilder().toFilter(), captureListener);
IFuture<Void> outerFuture = null;
final AtomicReference<IFuture<?>> innerFuture = new AtomicReference<>();
final JobInput modelJobInput = ModelJobs.newInput(ClientRunContexts.empty().withSession(clientSession, true));
// start recording of events
outerFuture = Jobs.getJobManager().schedule(new IRunnable() {
@Override
public void run() throws Exception {
innerFuture.set(Jobs.getJobManager().schedule(new IRunnable() {
@Override
public void run() throws Exception {
condition.setBlocking(false);
// Wait until the outer future is re-acquiring the mutex.
// 2=outer-job + inner-job
JobTestUtil.waitForPermitCompetitors(modelJobInput.getExecutionSemaphore(), 2);
}
}, modelJobInput.copy().withName("inner").withExecutionTrigger(Jobs.newExecutionTrigger().withStartIn(2, TimeUnit.SECONDS))));
condition.waitFor();
}
}, modelJobInput.copy().withName("outer"));
Jobs.getJobManager().awaitDone(Jobs.newFutureFilterBuilder().andMatchFuture(outerFuture).toFilter(), 1, TimeUnit.MINUTES);
Jobs.getJobManager().shutdown();
// verify events
int i = -1;
List<JobEvent> capturedEvents = captureListener.getCapturedEvents();
List<JobState> capturedFutureStates = captureListener.getCapturedFutureStates();
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.SCHEDULED, capturedEvents.get(i));
assertEquals(JobState.SCHEDULED, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.WAITING_FOR_PERMIT, capturedEvents.get(i));
assertEquals(JobState.WAITING_FOR_PERMIT, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.RUNNING, capturedEvents.get(i));
assertEquals(JobState.RUNNING, capturedFutureStates.get(i));
// inner
i++;
assertStateChangedEvent(innerFuture.get(), JobState.SCHEDULED, capturedEvents.get(i));
assertEquals(JobState.SCHEDULED, capturedFutureStates.get(i));
// inner
i++;
assertStateChangedEvent(innerFuture.get(), JobState.PENDING, capturedEvents.get(i));
assertEquals(JobState.PENDING, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.WAITING_FOR_BLOCKING_CONDITION, capturedEvents.get(i));
assertEquals(JobState.WAITING_FOR_BLOCKING_CONDITION, capturedFutureStates.get(i));
// inner
i++;
assertStateChangedEvent(innerFuture.get(), JobState.WAITING_FOR_PERMIT, capturedEvents.get(i));
assertEquals(JobState.WAITING_FOR_PERMIT, capturedFutureStates.get(i));
// inner
i++;
assertStateChangedEvent(innerFuture.get(), JobState.RUNNING, capturedEvents.get(i));
assertEquals(JobState.RUNNING, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.WAITING_FOR_PERMIT, capturedEvents.get(i));
assertEquals(JobState.WAITING_FOR_PERMIT, capturedFutureStates.get(i));
// inner
i++;
assertStateChangedEvent(innerFuture.get(), JobState.DONE, capturedEvents.get(i));
assertEquals(JobState.DONE, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.RUNNING, capturedEvents.get(i));
assertEquals(JobState.RUNNING, capturedFutureStates.get(i));
// outer
i++;
assertStateChangedEvent(outerFuture, JobState.DONE, capturedEvents.get(i));
assertEquals(JobState.DONE, capturedFutureStates.get(i));
assertEquals(i + 1, capturedEvents.size());
}
use of org.eclipse.scout.rt.platform.job.IFuture in project scout.rt by eclipse.
the class MutualExclusionTest method runTestBlockingCondition.
private void runTestBlockingCondition(final IBlockingCondition condition) {
condition.setBlocking(true);
// synchronized because modified/read by different threads.
final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
final IFuture<?> iFuture1 = IFuture.CURRENT.get();
protocol.add("job-1-running");
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
final IFuture<?> iFuture2 = IFuture.CURRENT.get();
protocol.add("job-2-running");
if (iFuture1.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-1-blocked");
}
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-3-running");
if (iFuture1.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-1-blocked");
}
if (iFuture2.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-2-blocked");
}
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-4-running");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
protocol.add("job-3-before-signaling");
condition.setBlocking(false);
// Wait for the other jobs to have tried re-acquiring the mutex.
JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 4);
protocol.add("job-3-after-signaling");
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-5-running");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
protocol.add("job-2-beforeAwait");
condition.waitFor();
protocol.add("JOB-X-AFTERAWAIT");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
protocol.add("job-1-beforeAwait");
condition.waitFor();
protocol.add("JOB-X-AFTERAWAIT");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
awaitDoneElseFail(JOB_IDENTIFIER);
List<String> expected = new ArrayList<>();
expected.add("job-1-running");
expected.add("job-1-beforeAwait");
expected.add("job-2-running");
expected.add("job-1-blocked");
expected.add("job-2-beforeAwait");
expected.add("job-3-running");
expected.add("job-1-blocked");
expected.add("job-2-blocked");
expected.add("job-3-before-signaling");
expected.add("job-3-after-signaling");
// no guarantee about the sequence in which waiting threads are released when the blocking condition falls.
expected.add("JOB-X-AFTERAWAIT");
// no guarantee about the sequence in which waiting threads are released when the blocking condition falls.
expected.add("JOB-X-AFTERAWAIT");
expected.add("job-4-running");
expected.add("job-5-running");
assertEquals(expected, protocol);
}
use of org.eclipse.scout.rt.platform.job.IFuture in project scout.rt by eclipse.
the class MutualExclusionTest method testMutexDeadlock.
/**
* Tests that a model-job cannot wait for a scheduled job.
*/
@Test
public void testMutexDeadlock() {
// synchronized because modified/read by different threads.
final List<Integer> protocol = Collections.synchronizedList(new ArrayList<Integer>());
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add(1);
IFuture<Void> future = ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add(3);
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
try {
future.awaitDoneAndGet(1, TimeUnit.SECONDS, DefaultExceptionTranslator.class);
} catch (AssertionException e) {
protocol.add(2);
} catch (Exception e) {
protocol.add(4);
}
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER));
awaitDoneElseFail(JOB_IDENTIFIER);
assertEquals(CollectionUtility.arrayList(1, 2, 3), protocol);
}
use of org.eclipse.scout.rt.platform.job.IFuture in project scout.rt by eclipse.
the class MutualExclusionTest method testBlockedJobs.
/**
* We have 5 jobs that get scheduled simultaneously. The first waits some time so that job2, job3, job4 and job5 get
* queued. Job1 then enters a blocking condition, which allows job2 to run. But job2 gets rejected by the executor,
* which allows job3 to run. After job3 completes, job1 is resumed and continues running. After job1 complete, job4
* gets scheduled. Job4 in turn gets blocked, which prevents job5 from running.
*/
@Test
public void testBlockedJobs() throws java.lang.InterruptedException {
P_JobManager jobManager = new P_JobManager();
ExecutorService executorMock = jobManager.getExecutorMock();
IBean<IJobManager> jobManagerBean = JobTestUtil.replaceCurrentJobManager(jobManager);
try {
// synchronized because modified/read by different threads.
final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
// Executor mock
doAnswer(new Answer<Future>() {
@Override
public Future answer(InvocationOnMock invocation) throws Throwable {
final Runnable runnable = (Runnable) invocation.getArguments()[0];
// Reject job-2 from being scheduled
if (runnable instanceof JobFutureTask) {
JobFutureTask<?> futureTask = (JobFutureTask<?>) runnable;
if ("job-2".equals(futureTask.getJobInput().getName())) {
futureTask.reject();
return null;
}
}
s_executor.execute(new NamedThreadRunnable(runnable));
return null;
}
}).when(executorMock).execute(any(Runnable.class));
final BlockingCountDownLatch job4RunningLatch = new BlockingCountDownLatch(1);
final IBlockingCondition condition = Jobs.newBlockingCondition(true);
// Job-1
IFuture<Void> future1 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
// Wait until all 5 jobs are scheduled.
JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 5);
try {
protocol.add("running-job-1 (a)");
condition.waitFor();
protocol.add("running-job-1 (b)");
} catch (ProcessingException e) {
protocol.add("jobException");
}
if (ModelJobs.isModelThread()) {
protocol.add("running-job-1 (e) [model-thread]");
}
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-1").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
// Job-2
IFuture<Void> future2 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-2");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-2").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
// Job-3
IFuture<Void> future3 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-3 (a)");
condition.setBlocking(false);
// Wait until job-1 tried to re-acquire the mutex.
// 4 = job1(re-acquiring), job3(owner), job4, job5
JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 4);
protocol.add("running-job-3 (b)");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-3").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
// Job-4
IFuture<Void> future4 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-4");
try {
job4RunningLatch.countDownAndBlock();
} catch (java.lang.InterruptedException e) {
protocol.add("job-4 [interrupted]");
}
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-4").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
// Job-5
IFuture<Void> future5 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-5");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-5").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
assertTrue(job4RunningLatch.await());
try {
Jobs.getJobManager().awaitDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter(), 1, TimeUnit.MILLISECONDS);
// job-4 and job-5 are pending
fail("timeout expected");
} catch (TimedOutError e) {
// NOOP
}
// job-4 and job-5 are pending
assertFalse(Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter()));
List<String> expectedProtocol = new ArrayList<>();
expectedProtocol.add("running-job-1 (a)");
expectedProtocol.add("running-job-3 (a)");
expectedProtocol.add("running-job-3 (b)");
expectedProtocol.add("running-job-1 (b)");
expectedProtocol.add("running-job-1 (e) [model-thread]");
expectedProtocol.add("running-job-4");
assertEquals(expectedProtocol, protocol);
assertFalse(future1.isCancelled());
assertTrue(future1.isDone());
assertTrue(future2.isCancelled());
assertTrue(future2.isDone());
assertFalse(future3.isCancelled());
assertTrue(future3.isDone());
assertFalse(future4.isCancelled());
assertFalse(future4.isDone());
assertFalse(future5.isCancelled());
assertFalse(future5.isDone());
// cancel job4
future4.cancel(true);
awaitDoneElseFail(JOB_IDENTIFIER);
expectedProtocol.add("job-4 [interrupted]");
expectedProtocol.add("running-job-5");
assertEquals(expectedProtocol, protocol);
assertTrue(Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter()));
assertTrue(future4.isCancelled());
assertTrue(future4.isDone());
assertFalse(future5.isCancelled());
assertTrue(future5.isDone());
} finally {
JobTestUtil.unregisterAndShutdownJobManager(jobManagerBean);
}
}
use of org.eclipse.scout.rt.platform.job.IFuture in project scout.rt by eclipse.
the class MutualExclusionTest method testRejection.
/**
* We have 3 jobs that get scheduled simultaneously. The first waits some time so that job2 and job3 get queued. When
* job2 gets scheduled, it is rejected by the executor. This test verifies, that job2 still gets scheduled.
*/
@Test
public void testRejection() {
P_JobManager jobManager = new P_JobManager();
ExecutorService executorMock = jobManager.getExecutorMock();
IBean<IJobManager> jobManagerBean = JobTestUtil.replaceCurrentJobManager(jobManager);
try {
// synchronized because modified/read by different threads.
final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
// Executor mock
doAnswer(new Answer<Future>() {
@Override
public Future answer(InvocationOnMock invocation) throws Throwable {
final Runnable runnable = (Runnable) invocation.getArguments()[0];
// Reject job-2 from being scheduled
if (runnable instanceof JobFutureTask) {
JobFutureTask<?> futureTask = (JobFutureTask<?>) runnable;
if ("job-2".equals(futureTask.getJobInput().getName())) {
futureTask.reject();
return null;
}
}
s_executor.execute(new NamedThreadRunnable(runnable));
return null;
}
}).when(executorMock).execute(any(Runnable.class));
// Job-1
final IFuture<Void> future1 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
// Wait until all 3 jobs are scheduled.
JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 3);
protocol.add("running-job-1");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-1").withExecutionHint(JOB_IDENTIFIER));
// Job-2
final IFuture<Void> future2 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-2");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-2").withExecutionHint(JOB_IDENTIFIER));
// Job-3
IFuture<Void> future3 = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("running-job-3");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-3").withExecutionHint(JOB_IDENTIFIER));
awaitDoneElseFail(JOB_IDENTIFIER);
assertEquals(Arrays.asList("running-job-1", "running-job-3"), protocol);
assertTrue(Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter()));
assertFalse(future1.isCancelled());
assertTrue(future2.isCancelled());
assertFalse(future3.isCancelled());
} finally {
JobTestUtil.unregisterAndShutdownJobManager(jobManagerBean);
}
}
Aggregations