package org.squirrelframework.foundation.fsm.threadsafe; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import junit.framework.Assert; import org.junit.Test; import org.squirrelframework.foundation.fsm.AnonymousAction; import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory; import org.squirrelframework.foundation.fsm.UntypedStateMachine; import org.squirrelframework.foundation.fsm.UntypedStateMachineBuilder; public class DeadLockTest { private UntypedStateMachine[] getTestingFSMInstances() { UntypedStateMachineBuilder builder1 = StateMachineBuilderFactory.create(ConcurrentSimpleStateMachine.class); builder1.transition().from("A").to("B").on("FIRST").perform( new AnonymousAction<UntypedStateMachine, Object, Object, Object>() { @Override public void execute(Object from, Object to, Object event, Object context, UntypedStateMachine stateMachine) { try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } UntypedStateMachine fsm2 = (UntypedStateMachine)context; System.out.println("fsm2 current state: "+fsm2.getCurrentState()); } }); UntypedStateMachineBuilder builder2 = StateMachineBuilderFactory.create(ConcurrentSimpleStateMachine.class); builder2.transition().from("C").to("D").on("SECOND").perform( new AnonymousAction<UntypedStateMachine, Object, Object, Object>() { @Override public void execute(Object from, Object to, Object event, Object context, UntypedStateMachine stateMachine) { UntypedStateMachine fsm1 = (UntypedStateMachine)context; System.out.println("fsm1 current state: "+fsm1.getCurrentState()); } }); final UntypedStateMachine fsm1 = builder1.newStateMachine("A"); fsm1.start(); final UntypedStateMachine fsm2 = builder2.newStateMachine("C"); fsm2.start(); return new UntypedStateMachine[]{fsm1, fsm2}; } @Test public void testDeadLockProblem() { final UntypedStateMachine[] fsm = getTestingFSMInstances(); final CountDownLatch eventCondition = new CountDownLatch(2); new Thread(new Runnable() { @Override public void run() { fsm[0].fire("FIRST", fsm[1]); eventCondition.countDown(); } }, "Test-Thread-1").start(); new Thread(new Runnable() { @Override public void run() { fsm[1].fire("SECOND", fsm[0]); eventCondition.countDown(); } }, "Test-Thread-2").start(); try { boolean isJobDone = eventCondition.await(1000, TimeUnit.MILLISECONDS); Assert.assertTrue(isJobDone==false); // due to dead lock, job cannot be done properly } catch (InterruptedException e) { } } @Test(timeout=100) public void testDeadLockFixed() { final UntypedStateMachine[] fsm = getTestingFSMInstances(); final CountDownLatch eventCondition = new CountDownLatch(2); new Thread(new Runnable() { @Override public void run() { synchronized (fsm[0]) { synchronized (fsm[1]) { fsm[0].fire("FIRST", fsm[1]); } } eventCondition.countDown(); } }, "Test-Thread-1").start(); new Thread(new Runnable() { @Override public void run() { synchronized (fsm[0]) { synchronized (fsm[1]) { fsm[1].fire("SECOND", fsm[0]); } } eventCondition.countDown(); } }, "Test-Thread-2").start(); try { boolean isJobDone = eventCondition.await(50, TimeUnit.MILLISECONDS); Assert.assertTrue(isJobDone); } catch (InterruptedException e) { } } }