use of org.eclipse.scout.rt.platform.util.concurrent.IRunnable 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);
}
use of org.eclipse.scout.rt.platform.util.concurrent.IRunnable in project scout.rt by eclipse.
the class MutualExclusionTest method runTestBlockingConditionMultipleFlat.
private void runTestBlockingConditionMultipleFlat(final IBlockingCondition condition) {
condition.setBlocking(true);
// synchronized because modified/read by different threads.
final List<String> protocol = Collections.synchronizedList(new ArrayList<String>());
final IFuture<Void> future1 = ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-1-beforeAwait");
condition.waitFor();
protocol.add("job-X-afterAwait");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER).withName("job-1"));
final IFuture<Void> future2 = ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-2-beforeAwait");
condition.waitFor();
protocol.add("job-X-afterAwait");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER).withName("job-2"));
final IFuture<Void> future3 = ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-3-beforeAwait");
condition.waitFor();
protocol.add("job-X-afterAwait");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER).withName("job-3"));
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-4-running");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER).withName("job-4"));
ModelJobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
protocol.add("job-5-running");
if (future1.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-1-blocked");
}
if (future2.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-2-blocked");
}
if (future3.getState() == JobState.WAITING_FOR_BLOCKING_CONDITION) {
protocol.add("job-3-blocked");
}
protocol.add("job-5-signaling");
condition.setBlocking(false);
// Wait until the other jobs tried to re-acquire the mutex.
JobTestUtil.waitForPermitCompetitors(m_clientSession.getModelJobSemaphore(), 4);
if (future1.getState() == JobState.WAITING_FOR_PERMIT) {
protocol.add("job-1-unblocked");
}
if (future2.getState() == JobState.WAITING_FOR_PERMIT) {
protocol.add("job-2-unblocked");
}
if (future3.getState() == JobState.WAITING_FOR_PERMIT) {
protocol.add("job-3-unblocked");
}
if (!future1.isDone()) {
protocol.add("job-1-stillRunning");
}
if (!future2.isDone()) {
protocol.add("job-2-stillRunning");
}
if (!future3.isDone()) {
protocol.add("job-3-stillRunning");
}
protocol.add("job-5-ending");
}
}, ModelJobs.newInput(ClientRunContexts.copyCurrent()).withExecutionHint(JOB_IDENTIFIER).withName("job-5"));
awaitDoneElseFail(JOB_IDENTIFIER);
List<String> expected = new ArrayList<>();
expected.add("job-1-beforeAwait");
expected.add("job-2-beforeAwait");
expected.add("job-3-beforeAwait");
expected.add("job-4-running");
expected.add("job-5-running");
expected.add("job-1-blocked");
expected.add("job-2-blocked");
expected.add("job-3-blocked");
expected.add("job-5-signaling");
expected.add("job-1-unblocked");
expected.add("job-2-unblocked");
expected.add("job-3-unblocked");
expected.add("job-1-stillRunning");
expected.add("job-2-stillRunning");
expected.add("job-3-stillRunning");
expected.add("job-5-ending");
// 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");
// no guarantee about the sequence in which waiting threads are released when the blocking condition falls.
expected.add("job-X-afterAwait");
assertEquals(expected, protocol);
}
use of org.eclipse.scout.rt.platform.util.concurrent.IRunnable 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);
}
use of org.eclipse.scout.rt.platform.util.concurrent.IRunnable in project scout.rt by eclipse.
the class ClusterSynchronizationService method onMessage.
@Override
public void onMessage(IMessage<IClusterNotificationMessage> message) {
final IClusterNotificationMessage notificationMessage = message.getTransferObject();
if (isEnabled()) {
// Do not progress notifications sent by node itself
String originNode = notificationMessage.getProperties().getOriginNode();
if (m_nodeId.equals(originNode)) {
return;
}
getStatusInfoInternal().updateReceiveStatus(notificationMessage);
getStatusInfoInternal(notificationMessage.getNotification().getClass()).updateReceiveStatus(notificationMessage);
ServerRunContext serverRunContext = ServerRunContexts.empty();
serverRunContext.withSubject(m_subject);
serverRunContext.withSession(BEANS.get(ServerSessionProviderWithCache.class).provide(serverRunContext.copy()));
serverRunContext.run(new IRunnable() {
@Override
public void run() throws Exception {
NotificationHandlerRegistry reg = BEANS.get(NotificationHandlerRegistry.class);
reg.notifyNotificationHandlers(notificationMessage.getNotification());
}
});
}
}
use of org.eclipse.scout.rt.platform.util.concurrent.IRunnable in project scout.rt by eclipse.
the class SessionStore method startHousekeeping.
/**
* If the given client session is still active, schedule a job that checks whether it is still in use after some time
* (see {@link SessionStoreHousekeepingDelayProperty}). If not, it will be stopped and removed from the store. If the
* session is inactive from the beginning, it is just removed from the store.
* <p>
* <b>Important:</b>: This method must be called from within a lock!
*/
protected void startHousekeeping(final IClientSession clientSession) {
// No client session, no house keeping necessary
if (clientSession == null) {
return;
}
// If client session is already inactive, simply update the maps, but take no further action.
if (!clientSession.isActive()) {
LOG.info("Session housekeeping: Removing inactive client session with ID {} from store", clientSession.getId());
removeClientSession(clientSession);
return;
}
// Check if client session is still used after a few moments
LOG.debug("Session housekeeping: Schedule job for client session with ID {}", clientSession.getId());
final IFuture<Void> future = Jobs.schedule(new IRunnable() {
@Override
public void run() throws Exception {
doHousekeeping(clientSession);
}
}, Jobs.newInput().withName("Performing session housekeeping for client session with ID {}", clientSession.getId()).withExecutionTrigger(Jobs.newExecutionTrigger().withStartIn(CONFIG.getPropertyValue(SessionStoreHousekeepingDelayProperty.class), TimeUnit.SECONDS)));
// Put the future in a list, so we can cancel it if the session is requested again
m_housekeepingFutures.put(clientSession.getId(), future);
}
Aggregations