--- jsr166/src/test/tck/AbstractQueuedLongSynchronizerTest.java 2017/11/27 23:06:53 1.43 +++ jsr166/src/test/tck/AbstractQueuedLongSynchronizerTest.java 2019/08/15 14:56:32 1.50 @@ -12,11 +12,9 @@ import static java.util.concurrent.TimeU import java.util.Arrays; import java.util.Collection; import java.util.HashSet; -import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.locks.AbstractQueuedLongSynchronizer; import java.util.concurrent.locks.AbstractQueuedLongSynchronizer.ConditionObject; -import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestSuite; @@ -141,7 +139,7 @@ public class AbstractQueuedLongSynchroni long startTime = System.nanoTime(); while (!sync.isQueued(t)) { if (millisElapsedSince(startTime) > LONG_DELAY_MS) - throw new AssertionFailedError("timed out"); + throw new AssertionError("timed out"); Thread.yield(); } assertTrue(t.isAlive()); @@ -225,8 +223,8 @@ public class AbstractQueuedLongSynchroni assertTrue(c.await(timeoutMillis, MILLISECONDS)); break; case awaitNanos: - long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); - long nanosRemaining = c.awaitNanos(nanosTimeout); + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); assertTrue(nanosRemaining > 0); break; case awaitUntil: @@ -253,8 +251,8 @@ public class AbstractQueuedLongSynchroni break; case awaitNanos: startTime = System.nanoTime(); - long nanosTimeout = MILLISECONDS.toNanos(timeoutMillis); - long nanosRemaining = c.awaitNanos(nanosTimeout); + long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); + long nanosRemaining = c.awaitNanos(timeoutNanos); assertTrue(nanosRemaining <= 0); assertTrue(nanosRemaining > -MILLISECONDS.toNanos(LONG_DELAY_MS)); assertTrue(millisElapsedSince(startTime) >= timeoutMillis); @@ -1260,13 +1258,14 @@ public class AbstractQueuedLongSynchroni /** * Tests scenario for * JDK-8191937: Lost interrupt in AbstractQueuedSynchronizer when tryAcquire methods throw + * ant -Djsr166.tckTestClass=AbstractQueuedLongSynchronizerTest -Djsr166.methodFilter=testInterruptedFailingAcquire -Djsr166.runsPerTest=10000 tck */ - public void testInterruptedFailingAcquire() throws InterruptedException { + public void testInterruptedFailingAcquire() throws Throwable { final RuntimeException ex = new RuntimeException(); // A synchronizer only offering a choice of failure modes class Sync extends AbstractQueuedLongSynchronizer { - boolean pleaseThrow; + volatile boolean pleaseThrow; @Override protected boolean tryAcquire(long ignored) { if (pleaseThrow) throw ex; return false; @@ -1284,30 +1283,72 @@ public class AbstractQueuedLongSynchroni } final Sync s = new Sync(); - + final Action[] uninterruptibleAcquireMethods = { + () -> s.acquire(1), + () -> s.acquireShared(1), + // TODO: test interruptible acquire methods + }; + final Action[] releaseMethods = { + () -> s.release(1), + () -> s.releaseShared(1), + }; + final Action acquireMethod + = chooseRandomly(uninterruptibleAcquireMethods); + final Action releaseMethod + = chooseRandomly(releaseMethods); + + // From os_posix.cpp: + // + // NOTE that since there is no "lock" around the interrupt and + // is_interrupted operations, there is the possibility that the + // interrupted flag (in osThread) will be "false" but that the + // low-level events will be in the signaled state. This is + // intentional. The effect of this is that Object.wait() and + // LockSupport.park() will appear to have a spurious wakeup, which + // is allowed and not harmful, and the possibility is so rare that + // it is not worth the added complexity to add yet another lock. final Thread thread = newStartedThread(new CheckedRunnable() { public void realRun() { try { - if (ThreadLocalRandom.current().nextBoolean()) - s.acquire(1); - else - s.acquireShared(1); + acquireMethod.run(); shouldThrow(); } catch (Throwable t) { assertSame(ex, t); - assertTrue(Thread.interrupted()); + awaitInterrupted(); } }}); - waitForThreadToEnterWaitState(thread); - assertSame(thread, s.getFirstQueuedThread()); - assertTrue(s.hasQueuedPredecessors()); - assertTrue(s.hasQueuedThreads()); - assertEquals(1, s.getQueueLength()); + for (long startTime = 0L;; ) { + waitForThreadToEnterWaitState(thread); + if (s.getFirstQueuedThread() == thread + && s.hasQueuedPredecessors() + && s.hasQueuedThreads() + && s.getQueueLength() == 1) + break; + if (startTime == 0L) + startTime = System.nanoTime(); + else if (millisElapsedSince(startTime) > LONG_DELAY_MS) + fail("timed out waiting for AQS state: " + + "thread state=" + thread.getState() + + ", queued threads=" + s.getQueuedThreads()); + Thread.yield(); + } s.pleaseThrow = true; - thread.interrupt(); - s.release(1); + // release and interrupt, in random order + if (randomBoolean()) { + thread.interrupt(); + releaseMethod.run(); + } else { + releaseMethod.run(); + thread.interrupt(); + } awaitTermination(thread); + + assertNull(s.getFirstQueuedThread()); + assertFalse(s.hasQueuedPredecessors()); + assertFalse(s.hasQueuedThreads()); + assertEquals(0, s.getQueueLength()); + assertTrue(s.getQueuedThreads().isEmpty()); } }