public virtual void testConcurrentMessageCorrelationTwiceAndTreeCompaction()
        {
            runtimeService.StartProcessInstanceByKey("process");

            // trigger non-interrupting boundary event 1 that ends in a none end event immediately
            runtimeService.CorrelateMessage("Message2");

            // trigger non-interrupting boundary event 2 and wait before flush
            ThreadControl correlateThread = ExecuteControllableCommand(new ControllableMessageCorrelationCommand("Message1", false));

            correlateThread.ReportInterrupts();

            // stop correlation right before the flush
            correlateThread.WaitForSync();
            correlateThread.MakeContinueAndWaitForSync();

            // trigger tree compaction
            IList <ITask> tasks = taskService.CreateTaskQuery().ToList();

            foreach (ITask task in tasks)
            {
                taskService.Complete(task.Id);
            }

            // flush correlation
            correlateThread.WaitUntilDone();

            // the correlation should not have succeeded
            System.Exception exception = correlateThread.Exception;
            Assert.NotNull(exception);
            Assert.True(exception is OptimisticLockingException);
        }
        public virtual void testConcurrentEndExecutionListener()
        {
            InvocationLogListener.reset();

            // given a process instance
            runtimeService.StartProcessInstanceByKey("testProcess");

            IList <IExecution> tasks = runtimeService.CreateExecutionQuery() /*.MessageEventSubscriptionName("Message")*/.ToList();

            // two tasks waiting for the message
            Assert.AreEqual(2, tasks.Count);

            // start first thread and wait in the second execution end listener
            ThreadControl thread1 = ExecuteControllableCommand(new ControllableMessageEventReceivedCommand(tasks[0].Id, "Message", true));

            thread1.ReportInterrupts();
            thread1.WaitForSync();

            // the counting execution listener was executed on task 1
            Assert.AreEqual(1, InvocationLogListener.Invocations);

            // start second thread and complete the task
            ThreadControl thread2 = ExecuteControllableCommand(new ControllableMessageEventReceivedCommand(tasks[1].Id, "Message", false));

            thread2.WaitForSync();
            thread2.WaitUntilDone();

            // the counting execution listener was executed on task 1 and 2
            Assert.AreEqual(2, InvocationLogListener.Invocations);

            // continue with thread 1
            thread1.MakeContinueAndWaitForSync();

            // the counting execution listener was not executed again
            Assert.AreEqual(2, InvocationLogListener.Invocations);

            // try to complete thread 1
            thread1.WaitUntilDone();

            // thread 1 was rolled back with an optimistic locking exception
            System.Exception exception = thread1.Exception;
            Assert.NotNull(exception);
            Assert.True(exception is OptimisticLockingException);

            // the execution listener was not executed again
            Assert.AreEqual(2, InvocationLogListener.Invocations);
        }