< 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 >