Search in sources :

Example 6 with IBlockingCondition

use of org.eclipse.scout.rt.platform.job.IBlockingCondition in project scout.rt by eclipse.

the class MutualExclusionTest method testBlockingCondition_InterruptedWhileReAcquiringTheMutex.

/**
 * We have 3 jobs that are scheduled simultaneously. Thereby, job1 enters a blocking condition which in turn lets job2
 * run. Job2 unblocks job1 so that job1 is trying to re-acquire the mutex. While waiting for the mutex to be
 * available, job1 is interrupted due to a cancel-request of job2.<br/>
 * This test verifies, that job1 is interrupted, competes for the mutex anew and only continues once a permit becomes
 * available. Also, job3 must not start running as long as job1 did not complete.
 */
@Test
// regression
@Times(100)
public void testBlockingCondition_InterruptedWhileReAcquiringTheMutex() throws java.lang.InterruptedException {
    // synchronized because modified/read by different threads.
    final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
    final IBlockingCondition condition = Jobs.newBlockingCondition(true);
    final BlockingCountDownLatch latchJob2 = new BlockingCountDownLatch(1);
    final BlockingCountDownLatch job1FinishLatch = new BlockingCountDownLatch(1);
    final IFuture<Void> future1 = ModelJobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            protocol.add("running-1");
            try {
                protocol.add("before-blocking-1");
                condition.waitFor();
            } catch (ThreadInterruptedError e) {
                protocol.add("interrupted-1 (a)");
            } catch (RuntimeException e) {
                protocol.add("jobException-1");
            }
            if (Thread.currentThread().isInterrupted()) {
                protocol.add("interrupted-1 (b)");
            }
            if (ModelJobs.isModelThread()) {
                protocol.add("model-thread-1");
            }
            protocol.add("done-1");
            job1FinishLatch.countDown();
        }
    }, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-1").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
    final IFuture<Void> future2 = ModelJobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            protocol.add("running-2a");
            if (future1.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("job2: job-1-waiting-for-blocking-condition");
            }
            if (!future1.getExecutionSemaphore().isPermitOwner(future1)) {
                protocol.add("job2: job-1-not-permit-owner");
            }
            protocol.add("unblocking condition");
            condition.setBlocking(false);
            // job-1 (interrupted acquisition task), job-2 (latch), job-3 (waiting for mutex)
            JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 3);
            if (future1.getState() == JobState.WAITING_FOR_PERMIT) {
                protocol.add("job2: job-1-waiting-for-mutex");
            }
            protocol.add("before-cancel-job1-2");
            // interrupt job1 while acquiring the mutex
            future1.cancel(true);
            // job-1 (interrupted acquisition task), job-1 (re-acquiring a permit), job-2 (latch), job-3 (waiting for mutex)
            JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 4);
            // cancelled, but still running
            JobTestUtil.waitForState(future1, JobState.DONE);
            protocol.add("running-2b");
            latchJob2.countDownAndBlock();
            protocol.add("done-2");
        }
    }, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-2").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
    final IFuture<Void> future3 = ModelJobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            protocol.add("done-3");
        }
    }, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-3").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
    assertTrue(latchJob2.await());
    assertEquals(future1.getState(), JobState.DONE);
    assertEquals(future2.getState(), JobState.RUNNING);
    assertEquals(future3.getState(), JobState.WAITING_FOR_PERMIT);
    latchJob2.unblock();
    Jobs.getJobManager().awaitDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter(), 5, TimeUnit.SECONDS);
    List<String> expectedProtocol = new ArrayList<>();
    expectedProtocol.add("running-1");
    expectedProtocol.add("before-blocking-1");
    expectedProtocol.add("running-2a");
    expectedProtocol.add("job2: job-1-waiting-for-blocking-condition");
    expectedProtocol.add("job2: job-1-not-permit-owner");
    expectedProtocol.add("unblocking condition");
    expectedProtocol.add("job2: job-1-waiting-for-mutex");
    expectedProtocol.add("before-cancel-job1-2");
    expectedProtocol.add("running-2b");
    expectedProtocol.add("done-2");
    expectedProtocol.add("interrupted-1 (b)");
    expectedProtocol.add("model-thread-1");
    expectedProtocol.add("done-1");
    expectedProtocol.add("done-3");
    assertEquals(expectedProtocol, protocol);
    assertEquals(future1.getState(), JobState.DONE);
    assertEquals(future2.getState(), JobState.DONE);
    assertEquals(future3.getState(), JobState.DONE);
    assertTrue(future1.isCancelled());
    future1.awaitDone(1, TimeUnit.NANOSECONDS);
    assertFalse(future2.isCancelled());
    future2.awaitDone(1, TimeUnit.NANOSECONDS);
    assertFalse(future3.isCancelled());
    future3.awaitDone(1, TimeUnit.NANOSECONDS);
}
Also used : BlockingCountDownLatch(org.eclipse.scout.rt.testing.platform.util.BlockingCountDownLatch) ArrayList(java.util.ArrayList) ThreadInterruptedError(org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError) IRunnable(org.eclipse.scout.rt.platform.util.concurrent.IRunnable) ProcessingException(org.eclipse.scout.rt.platform.exception.ProcessingException) AssertionException(org.eclipse.scout.rt.platform.util.Assertions.AssertionException) IBlockingCondition(org.eclipse.scout.rt.platform.job.IBlockingCondition) Test(org.junit.Test) Times(org.eclipse.scout.rt.testing.platform.runner.Times)

Example 7 with IBlockingCondition

use of org.eclipse.scout.rt.platform.job.IBlockingCondition in project scout.rt by eclipse.

the class MutualExclusionTest method testBlockingConditionMultipleNested.

/**
 * Tests a BlockingCondition that blocks multiple model-threads that were scheduled as nested jobs.
 */
@Test
public void testBlockingConditionMultipleNested() {
    final IBlockingCondition condition = Jobs.newBlockingCondition(true);
    // run the test 2 times to also test reusability of a blocking condition.
    runTestBlockingCondition(condition);
    runTestBlockingCondition(condition);
}
Also used : IBlockingCondition(org.eclipse.scout.rt.platform.job.IBlockingCondition) Test(org.junit.Test)

Example 8 with IBlockingCondition

use of org.eclipse.scout.rt.platform.job.IBlockingCondition in project scout.rt by eclipse.

the class MutualExclusionTest method testBlockingConditionSingle.

/**
 * Tests a BlockingCondition that blocks a single model-thread.
 */
@Test
public void testBlockingConditionSingle() {
    // synchronized because modified/read by different threads.
    final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
    final IBlockingCondition condition = Jobs.newBlockingCondition(true);
    final IFuture<Void> future1 = ModelJobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            protocol.add("1: running");
            if (Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter())) {
                protocol.add("1: idle [a]");
            }
            if (IFuture.CURRENT.get().getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("1: blocked [a]");
            }
            if (ModelJobs.isModelThread()) {
                protocol.add("1: modelThread [a]");
            }
            protocol.add("1: beforeAwait");
            condition.waitFor();
            protocol.add("1: afterAwait");
            if (IFuture.CURRENT.get().getState() == JobState.RUNNING) {
                protocol.add("1: state=running");
            }
            if (Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter())) {
                protocol.add("1: idle [b]");
            }
            if (IFuture.CURRENT.get().getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("1: blocked [b]");
            }
            if (ModelJobs.isModelThread()) {
                protocol.add("1: modelThread [b]");
            }
        }
    }, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-1").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
    ModelJobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            protocol.add("2: running");
            if (Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter())) {
                protocol.add("2: idle [a]");
            }
            if (future1.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("2: job-1-state: waiting-for-condition");
            }
            if (IFuture.CURRENT.get().getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("2: blocked [a]");
            }
            if (ModelJobs.isModelThread()) {
                protocol.add("2: modelThread [a]");
            }
            // RELEASE THE BlockingCondition
            protocol.add("2: beforeSignaling");
            condition.setBlocking(false);
            protocol.add("2: afterSignaling");
            // Wait until job1 is competing for the mutex anew
            JobTestUtil.waitForPermitCompetitors(ClientRunContexts.copyCurrent().getSession().getModelJobSemaphore(), 2);
            if (future1.getState() == JobState.WAITING_FOR_PERMIT) {
                protocol.add("2: job-1-state: waiting-for-mutex");
            }
            if (Jobs.getJobManager().isDone(Jobs.newFutureFilterBuilder().andMatchExecutionHint(JOB_IDENTIFIER).toFilter())) {
                protocol.add("2: idle [b]");
            }
            if (IFuture.CURRENT.get().getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
                protocol.add("2: blocked [b]");
            }
            if (ModelJobs.isModelThread()) {
                protocol.add("2: modelThread [b]");
            }
        }
    }, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("job-2").withExecutionHint(JOB_IDENTIFIER).withExceptionHandling(null, false));
    // Wait until job1 completed.
    future1.awaitDoneAndGet(30, TimeUnit.SECONDS);
    awaitDoneElseFail(JOB_IDENTIFIER);
    List<String> expected = new ArrayList<>();
    expected.add("1: running");
    expected.add("1: modelThread [a]");
    expected.add("1: beforeAwait");
    expected.add("2: running");
    expected.add("2: job-1-state: waiting-for-condition");
    expected.add("2: modelThread [a]");
    expected.add("2: beforeSignaling");
    expected.add("2: afterSignaling");
    expected.add("2: job-1-state: waiting-for-mutex");
    expected.add("2: modelThread [b]");
    expected.add("1: afterAwait");
    expected.add("1: state=running");
    expected.add("1: modelThread [b]");
    assertEquals(expected, protocol);
}
Also used : ArrayList(java.util.ArrayList) IRunnable(org.eclipse.scout.rt.platform.util.concurrent.IRunnable) IBlockingCondition(org.eclipse.scout.rt.platform.job.IBlockingCondition) ProcessingException(org.eclipse.scout.rt.platform.exception.ProcessingException) AssertionException(org.eclipse.scout.rt.platform.util.Assertions.AssertionException) Test(org.junit.Test)

Example 9 with IBlockingCondition

use of org.eclipse.scout.rt.platform.job.IBlockingCondition in project scout.rt by eclipse.

the class ModelJobs method yield.

/**
 * Instructs the job manager that the current model job is willing to temporarily yield its current model job permit.
 * <p>
 * It is rarely appropriate to use this method. It may be useful for debugging or testing purposes.
 */
public static void yield() {
    Assertions.assertTrue(ModelJobs.isModelThread(), "'Yield' must be invoked from model thread");
    final IBlockingCondition idleCondition = Jobs.newBlockingCondition(true);
    ModelJobs.schedule(NULL_RUNNABLE, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withName("Technical job to yield model thread")).whenDone(new IDoneHandler<Void>() {

        @Override
        public void onDone(final DoneEvent<Void> event) {
            idleCondition.setBlocking(false);
        }
    }, ClientRunContexts.copyCurrent());
    // Release the current model job permit and wait until all competing model jobs of this session completed their work.
    idleCondition.waitFor();
}
Also used : IBlockingCondition(org.eclipse.scout.rt.platform.job.IBlockingCondition)

Example 10 with IBlockingCondition

use of org.eclipse.scout.rt.platform.job.IBlockingCondition in project scout.rt by eclipse.

the class ExecutionSemaphoreTest method testInternalDeadlock.

/**
 * Tests an internal of {@link ExecutionSemaphore}, that {@link AcquisitionTask#notifyPermitAcquired()} is invoked
 * outside the {@link ExecutionSemaphore} lock.
 * <p>
 * Otherwise, a deadlock might occur, once the resuming job-1 tries to re-acquire the permit, namely exactly the time
 * when owning acquisitionLock in {@link ExecutionSemaphore#acquire(IFuture, QueuePosition)} and querying
 * 'isPermitOwner'. Thereto, job-1 must compete for the semaphore lock, while job-2 (owning semaphore lock) tries to
 * notify the resuming job-1 via {@link AcquisitionTask#notifyPermitAcquired()}, but cannot get monitor of
 * acquisitionLock.
 */
@Test
// regression; do not remove
@Times(1_000)
public void testInternalDeadlock() {
    final IExecutionSemaphore semaphore = Jobs.newExecutionSemaphore(1);
    final IBlockingCondition condition = Jobs.newBlockingCondition(true);
    final AtomicReference<IFuture<?>> future2Ref = new AtomicReference<>();
    IFuture<Void> future1 = Jobs.schedule(new IRunnable() {

        @Override
        public void run() throws Exception {
            future2Ref.set(Jobs.schedule(new IRunnable() {

                @Override
                public void run() throws Exception {
                    condition.setBlocking(false);
                }
            }, Jobs.newInput().withName("job-2").withExecutionSemaphore(semaphore)));
            condition.waitFor();
        }
    }, Jobs.newInput().withName("job-1").withExecutionSemaphore(semaphore));
    try {
        future1.awaitDoneAndGet(5, TimeUnit.SECONDS);
    } catch (TimedOutError e) {
        fail(String.format("Deadlock while passing permit from 'job-2' to 'job-1' [job-1-state=%s, job-2-state=%s", future1.getState(), future2Ref.get().getState()));
    }
}
Also used : AtomicReference(java.util.concurrent.atomic.AtomicReference) IExecutionSemaphore(org.eclipse.scout.rt.platform.job.IExecutionSemaphore) IRunnable(org.eclipse.scout.rt.platform.util.concurrent.IRunnable) TimedOutError(org.eclipse.scout.rt.platform.util.concurrent.TimedOutError) IFuture(org.eclipse.scout.rt.platform.job.IFuture) IBlockingCondition(org.eclipse.scout.rt.platform.job.IBlockingCondition) AssertionException(org.eclipse.scout.rt.platform.util.Assertions.AssertionException) Test(org.junit.Test) Times(org.eclipse.scout.rt.testing.platform.runner.Times)

Aggregations

IBlockingCondition (org.eclipse.scout.rt.platform.job.IBlockingCondition)32 Test (org.junit.Test)24 IRunnable (org.eclipse.scout.rt.platform.util.concurrent.IRunnable)20 AssertionException (org.eclipse.scout.rt.platform.util.Assertions.AssertionException)12 ProcessingException (org.eclipse.scout.rt.platform.exception.ProcessingException)10 BlockingCountDownLatch (org.eclipse.scout.rt.testing.platform.util.BlockingCountDownLatch)9 ArrayList (java.util.ArrayList)8 IExecutionSemaphore (org.eclipse.scout.rt.platform.job.IExecutionSemaphore)6 AtomicReference (java.util.concurrent.atomic.AtomicReference)5 PlatformException (org.eclipse.scout.rt.platform.exception.PlatformException)4 IFuture (org.eclipse.scout.rt.platform.job.IFuture)4 TimedOutError (org.eclipse.scout.rt.platform.util.concurrent.TimedOutError)4 Times (org.eclipse.scout.rt.testing.platform.runner.Times)4 ClientNotificationDispatcher (org.eclipse.scout.rt.client.clientnotification.ClientNotificationDispatcher)3 ThreadInterruptedError (org.eclipse.scout.rt.platform.util.concurrent.ThreadInterruptedError)3 PropertyChangeEvent (java.beans.PropertyChangeEvent)2 PropertyChangeListener (java.beans.PropertyChangeListener)2 Serializable (java.io.Serializable)2 List (java.util.List)2 ClientRunContext (org.eclipse.scout.rt.client.context.ClientRunContext)2