/* * JCarder -- cards Java programs to keep threads disentangled * * Copyright (C) 2006-2007 Enea AB * Copyright (C) 2007 Ulrik Svensson * Copyright (C) 2007 Joel Rosdahl * * This program is made available under the GNU GPL version 2, with a special * exception for linking with JUnit. See the accompanying file LICENSE.txt for * details. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. */ package com.enea.jcarder.testclasses.agent; import com.enea.jcarder.agent.LockEvent; import com.enea.jcarder.common.Lock; import com.enea.jcarder.common.LockingContext; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public final class ComparableAlternativeSynchronizationRoutes implements SynchronizationTestIfc { private final Object mSync0 = new Object(); private final Object mSync1 = new Object(); private final Object mSync2 = new Object(); public void go() { assertFalse(Thread.holdsLock(mSync0)); synchronized (mSync0) { assertTrue(Thread.holdsLock(mSync0)); assertFalse(Thread.holdsLock(mSync1)); synchronized (mSync1) { assertTrue(Thread.holdsLock(mSync1)); assertFalse(Thread.holdsLock(mSync2)); synchronized (mSync2) { assertTrue(Thread.holdsLock(mSync2)); } } assertFalse(Thread.holdsLock(mSync1)); assertFalse(Thread.holdsLock(mSync2)); // The transition from mSync0 -> mSync2 is already // "represented" as mSync0 -> mSync1 -> mSync2 which is enough // for the cycle-detection algorithm, but the following transition // is also included since the user might want to see a "complete" // graph with all transitions. // // If the number of events needs to be reduced it might be possible // to make this behaviour configurable for the user. synchronized (mSync2) { assertTrue(Thread.holdsLock(mSync2)); } } assertFalse(Thread.holdsLock(mSync0)); assertFalse(Thread.holdsLock(mSync1)); assertFalse(Thread.holdsLock(mSync2)); } /* If the behaviour is made configurable to the user as described above it might be interesting to also optimze the following scenarios: public void foo1() { synchronized(a) { synchronized(b) { synchronized(c) { ... } // The following duplicated a -> b transition was implicitly // lost/ignored with the old implementation when the onLockEvent was // sent AFTER instead of BEFORE the monitor was entered. synchronized(b) { ... } } } // A more generic example than foo1(). public void foo2() { synchronized(a) { synchronized(b) { ... } } // A possibility to minimize repeated groups of transitions could be // to not remove monitors from the stack of aquired monitors when it // is not needed and instead keep a pointer the the current last // taken monitor. synchronized(a) { synchronized(b) { ... } } } */ public LockEvent[] getExpectedLockEvents() { final Lock lockSync0 = new Lock(mSync0); final Lock lockSync1 = new Lock(mSync1); final Lock lockSync2 = new Lock(mSync2); final String threadName = Thread.currentThread().getName(); final String method = getClass().getName() + ".go()"; LockingContext contextSync0 = new LockingContext(threadName, getClass().getName() + ".mSync0", method); LockingContext contextSync1 = new LockingContext(threadName, getClass().getName() + ".mSync1", method); LockingContext contextSync2 = new LockingContext(threadName, getClass().getName() + ".mSync2", method); return new LockEvent[] { new LockEvent(lockSync1, contextSync1, lockSync0, contextSync0), new LockEvent(lockSync2, contextSync2, lockSync1, contextSync1), new LockEvent(lockSync2, contextSync2, lockSync0, contextSync0), }; } }