--- jsr166/src/test/tck/AbstractQueuedSynchronizerTest.java 2017/05/14 02:16:56 1.54 +++ jsr166/src/test/tck/AbstractQueuedSynchronizerTest.java 2017/11/27 03:25:42 1.59 @@ -9,6 +9,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.NANOSECONDS; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; @@ -19,6 +20,7 @@ import junit.framework.AssertionFailedEr import junit.framework.Test; import junit.framework.TestSuite; +@SuppressWarnings("WaitNotInLoop") // we implement spurious-wakeup freedom public class AbstractQueuedSynchronizerTest extends JSR166TestCase { public static void main(String[] args) { main(suite(), args); @@ -33,6 +35,9 @@ public class AbstractQueuedSynchronizerT * methods/features of AbstractQueuedSynchronizer are tested via * other test classes, including those for ReentrantLock, * ReentrantReadWriteLock, and Semaphore. + * + * Unlike the javadoc sample, we don't track owner thread via + * AbstractOwnableSynchronizer methods. */ static class Mutex extends AbstractQueuedSynchronizer { /** An eccentric value for locked synchronizer state. */ @@ -40,18 +45,19 @@ public class AbstractQueuedSynchronizerT static final int UNLOCKED = 0; + /** Owner thread is untracked, so this is really just isLocked(). */ @Override public boolean isHeldExclusively() { int state = getState(); assertTrue(state == UNLOCKED || state == LOCKED); return state == LOCKED; } - @Override public boolean tryAcquire(int acquires) { + @Override protected boolean tryAcquire(int acquires) { assertEquals(LOCKED, acquires); return compareAndSetState(UNLOCKED, LOCKED); } - @Override public boolean tryRelease(int releases) { + @Override protected boolean tryRelease(int releases) { if (getState() != LOCKED) throw new IllegalMonitorStateException(); assertEquals(LOCKED, releases); setState(UNLOCKED); @@ -82,13 +88,14 @@ public class AbstractQueuedSynchronizerT release(LOCKED); } + /** Faux-Implements Lock.newCondition(). */ public ConditionObject newCondition() { return new ConditionObject(); } } /** - * A simple latch class, to test shared mode. + * A minimal latch class, to test shared mode. */ static class BooleanLatch extends AbstractQueuedSynchronizer { public boolean isSignalled() { return getState() != 0; } @@ -1134,7 +1141,7 @@ public class AbstractQueuedSynchronizerT waitForQueuedThread(l, t); assertFalse(l.isSignalled()); - assertThreadStaysAlive(t); + assertThreadBlocks(t, Thread.State.WAITING); assertHasSharedQueuedThreads(l, t); assertTrue(l.releaseShared(0)); assertTrue(l.isSignalled()); @@ -1159,7 +1166,7 @@ public class AbstractQueuedSynchronizerT waitForQueuedThread(l, t); assertFalse(l.isSignalled()); - assertThreadStaysAlive(t); + assertThreadBlocks(t, Thread.State.TIMED_WAITING); assertTrue(l.releaseShared(0)); assertTrue(l.isSignalled()); awaitTermination(t); @@ -1253,4 +1260,55 @@ public class AbstractQueuedSynchronizerT sync.release(); } + /** + * Disabled demo test for (unfixed as of 2017-11) + * JDK-8191483: AbstractQueuedSynchronizer cancel/cancel race + * ant -Djsr166.tckTestClass=AbstractQueuedSynchronizerTest -Djsr166.methodFilter=testCancelCancelRace -Djsr166.runsPerTest=100 tck + */ + public void XXXXtestCancelCancelRace() throws InterruptedException { + class Sync extends AbstractQueuedSynchronizer { + private static final long serialVersionUID = 1L; + + public boolean tryAcquire(int acquires) { + return !hasQueuedPredecessors() && compareAndSetState(0, 1); + } + + protected boolean tryRelease(int releases) { + return compareAndSetState(1, 0); + } + } + + Sync s = new Sync(); + s.acquire(1); // acquire to force other threads to enqueue + + // try to trigger double cancel race with two background threads + ArrayList ts = new ArrayList<>(); + Runnable failedAcquire = () -> { + try { + s.acquireInterruptibly(1); + throw new AssertionError(); + } catch (InterruptedException expected) {} + }; + for (int i = 0; i < 2; i++) { + Thread t = new Thread(failedAcquire); + t.start(); + ts.add(t); + } + Thread.sleep(100); + for (Thread t : ts) t.interrupt(); + for (Thread t : ts) t.join(); + + s.release(1); + + // no one holds lock now, we should be able to acquire + if (!s.tryAcquire(1)) + throw new RuntimeException( + String.format( + "Broken: hasQueuedPredecessors=%s hasQueuedThreads=%s queueLength=%d firstQueuedThread=%s", + s.hasQueuedPredecessors(), + s.hasQueuedThreads(), + s.getQueueLength(), + s.getFirstQueuedThread())); + } + }