--- jsr166/src/test/tck/AbstractQueuedSynchronizerTest.java 2017/11/27 03:25:42 1.59 +++ jsr166/src/test/tck/AbstractQueuedSynchronizerTest.java 2017/11/27 23:06:53 1.60 @@ -13,6 +13,7 @@ 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.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject; @@ -1267,12 +1268,9 @@ public class AbstractQueuedSynchronizerT */ public void XXXXtestCancelCancelRace() throws InterruptedException { class Sync extends AbstractQueuedSynchronizer { - private static final long serialVersionUID = 1L; - - public boolean tryAcquire(int acquires) { + protected boolean tryAcquire(int acquires) { return !hasQueuedPredecessors() && compareAndSetState(0, 1); } - protected boolean tryRelease(int releases) { return compareAndSetState(1, 0); } @@ -1282,21 +1280,21 @@ public class AbstractQueuedSynchronizerT s.acquire(1); // acquire to force other threads to enqueue // try to trigger double cancel race with two background threads - ArrayList ts = new ArrayList<>(); + ArrayList threads = new ArrayList<>(); Runnable failedAcquire = () -> { try { s.acquireInterruptibly(1); - throw new AssertionError(); + shouldThrow(); } catch (InterruptedException expected) {} }; for (int i = 0; i < 2; i++) { - Thread t = new Thread(failedAcquire); - t.start(); - ts.add(t); + Thread thread = new Thread(failedAcquire); + thread.start(); + threads.add(thread); } Thread.sleep(100); - for (Thread t : ts) t.interrupt(); - for (Thread t : ts) t.join(); + for (Thread thread : threads) thread.interrupt(); + for (Thread thread : threads) awaitTermination(thread); s.release(1); @@ -1311,4 +1309,57 @@ public class AbstractQueuedSynchronizerT s.getFirstQueuedThread())); } + /** + * Tests scenario for + * JDK-8191937: Lost interrupt in AbstractQueuedSynchronizer when tryAcquire methods throw + */ + public void testInterruptedFailingAcquire() throws InterruptedException { + final RuntimeException ex = new RuntimeException(); + + // A synchronizer only offering a choice of failure modes + class Sync extends AbstractQueuedSynchronizer { + boolean pleaseThrow; + @Override protected boolean tryAcquire(int ignored) { + if (pleaseThrow) throw ex; + return false; + } + @Override protected int tryAcquireShared(int ignored) { + if (pleaseThrow) throw ex; + return -1; + } + @Override protected boolean tryRelease(int ignored) { + return true; + } + @Override protected boolean tryReleaseShared(int ignored) { + return true; + } + } + + final Sync s = new Sync(); + + final Thread thread = newStartedThread(new CheckedRunnable() { + public void realRun() { + try { + if (ThreadLocalRandom.current().nextBoolean()) + s.acquire(1); + else + s.acquireShared(1); + shouldThrow(); + } catch (Throwable t) { + assertSame(ex, t); + assertTrue(Thread.interrupted()); + } + }}); + waitForThreadToEnterWaitState(thread); + assertSame(thread, s.getFirstQueuedThread()); + assertTrue(s.hasQueuedPredecessors()); + assertTrue(s.hasQueuedThreads()); + assertEquals(1, s.getQueueLength()); + + s.pleaseThrow = true; + thread.interrupt(); + s.release(1); + awaitTermination(thread); + } + }