< prev index next >
test/jdk/java/util/concurrent/tck/AbstractQueuedSynchronizerTest.java
Print this page
8225490: Miscellaneous changes imported from jsr166 CVS 2019-09
Reviewed-by: martin, alanb
@@ -38,11 +38,11 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
-import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject;
import junit.framework.Test;
import junit.framework.TestSuite;
@@ -1335,23 +1335,35 @@
}
/**
* Tests scenario for
* JDK-8191937: Lost interrupt in AbstractQueuedSynchronizer when tryAcquire methods throw
+ * ant -Djsr166.tckTestClass=AbstractQueuedSynchronizerTest -Djsr166.methodFilter=testInterruptedFailingAcquire -Djsr166.runsPerTest=10000 tck
*/
- public void testInterruptedFailingAcquire() throws InterruptedException {
- final RuntimeException ex = new RuntimeException();
+ public void testInterruptedFailingAcquire() throws Throwable {
+ class PleaseThrow extends RuntimeException {}
+ final PleaseThrow ex = new PleaseThrow();
+ final AtomicBoolean thrown = new AtomicBoolean();
// A synchronizer only offering a choice of failure modes
class Sync extends AbstractQueuedSynchronizer {
- boolean pleaseThrow;
+ volatile boolean pleaseThrow;
+ void maybeThrow() {
+ if (pleaseThrow) {
+ // assert: tryAcquire methods can throw at most once
+ if (! thrown.compareAndSet(false, true))
+ throw new AssertionError();
+ throw ex;
+ }
+ }
+
@Override protected boolean tryAcquire(int ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return false;
}
@Override protected int tryAcquireShared(int ignored) {
- if (pleaseThrow) throw ex;
+ maybeThrow();
return -1;
}
@Override protected boolean tryRelease(int ignored) {
return true;
}
@@ -1359,32 +1371,89 @@
return true;
}
}
final Sync s = new Sync();
-
+ final boolean acquireInterruptibly = randomBoolean();
+ final Action[] uninterruptibleAcquireActions = {
+ () -> s.acquire(1),
+ () -> s.acquireShared(1),
+ };
+ final long nanosTimeout = MILLISECONDS.toNanos(2 * LONG_DELAY_MS);
+ final Action[] interruptibleAcquireActions = {
+ () -> s.acquireInterruptibly(1),
+ () -> s.acquireSharedInterruptibly(1),
+ () -> s.tryAcquireNanos(1, nanosTimeout),
+ () -> s.tryAcquireSharedNanos(1, nanosTimeout),
+ };
+ final Action[] releaseActions = {
+ () -> s.release(1),
+ () -> s.releaseShared(1),
+ };
+ final Action acquireAction = acquireInterruptibly
+ ? chooseRandomly(interruptibleAcquireActions)
+ : chooseRandomly(uninterruptibleAcquireActions);
+ final Action releaseAction
+ = chooseRandomly(releaseActions);
+
+ // 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() {
+ public void realRun() throws Throwable {
try {
- if (ThreadLocalRandom.current().nextBoolean())
- s.acquire(1);
- else
- s.acquireShared(1);
+ acquireAction.run();
shouldThrow();
- } catch (Throwable t) {
- assertSame(ex, t);
- assertTrue(Thread.interrupted());
+ } catch (InterruptedException possible) {
+ assertTrue(acquireInterruptibly);
+ assertFalse(Thread.interrupted());
+ } catch (PleaseThrow possible) {
+ awaitInterrupted();
}
}});
+ for (long startTime = 0L;; ) {
waitForThreadToEnterWaitState(thread);
- assertSame(thread, s.getFirstQueuedThread());
- assertTrue(s.hasQueuedPredecessors());
- assertTrue(s.hasQueuedThreads());
- assertEquals(1, s.getQueueLength());
+ if (s.getFirstQueuedThread() == thread
+ && s.hasQueuedPredecessors()
+ && s.hasQueuedThreads()
+ && s.getQueueLength() == 1
+ && s.hasContended())
+ 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;
+ // release and interrupt, in random order
+ if (randomBoolean()) {
thread.interrupt();
- s.release(1);
+ releaseAction.run();
+ } else {
+ releaseAction.run();
+ thread.interrupt();
+ }
awaitTermination(thread);
+
+ if (! acquireInterruptibly)
+ assertTrue(thrown.get());
+
+ assertNull(s.getFirstQueuedThread());
+ assertFalse(s.hasQueuedPredecessors());
+ assertFalse(s.hasQueuedThreads());
+ assertEquals(0, s.getQueueLength());
+ assertTrue(s.getQueuedThreads().isEmpty());
+ assertTrue(s.hasContended());
}
}
< prev index next >