--- jsr166/src/test/tck/AbstractQueuedLongSynchronizerTest.java 2017/07/17 21:01:30 1.41 +++ jsr166/src/test/tck/AbstractQueuedLongSynchronizerTest.java 2019/08/13 23:05:18 1.48 @@ -15,7 +15,6 @@ import java.util.HashSet; 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; @@ -31,6 +30,9 @@ public class AbstractQueuedLongSynchroni /** * A simple mutex class, adapted from the class javadoc. Exclusive * acquire tests exercise this as a sample user extension. + * + * Unlike the javadoc sample, we don't track owner thread via + * AbstractOwnableSynchronizer methods. */ static class Mutex extends AbstractQueuedLongSynchronizer { /** An eccentric value > 32 bits for locked synchronizer state. */ @@ -38,18 +40,19 @@ public class AbstractQueuedLongSynchroni static final long UNLOCKED = 0; - public boolean isHeldExclusively() { + /** Owner thread is untracked, so this is really just isLocked(). */ + @Override public boolean isHeldExclusively() { long state = getState(); assertTrue(state == UNLOCKED || state == LOCKED); return state == LOCKED; } - public boolean tryAcquire(long acquires) { + @Override protected boolean tryAcquire(long acquires) { assertEquals(LOCKED, acquires); return compareAndSetState(UNLOCKED, LOCKED); } - public boolean tryRelease(long releases) { + @Override protected boolean tryRelease(long releases) { if (getState() != LOCKED) throw new IllegalMonitorStateException(); setState(UNLOCKED); return true; @@ -79,13 +82,14 @@ public class AbstractQueuedLongSynchroni 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 AbstractQueuedLongSynchronizer { public boolean isSignalled() { return getState() != 0; } @@ -135,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()); @@ -219,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: @@ -247,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); @@ -1251,4 +1255,57 @@ public class AbstractQueuedLongSynchroni sync.release(); } + /** + * 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 AbstractQueuedLongSynchronizer { + volatile boolean pleaseThrow; + @Override protected boolean tryAcquire(long ignored) { + if (pleaseThrow) throw ex; + return false; + } + @Override protected long tryAcquireShared(long ignored) { + if (pleaseThrow) throw ex; + return -1; + } + @Override protected boolean tryRelease(long ignored) { + return true; + } + @Override protected boolean tryReleaseShared(long ignored) { + return true; + } + } + + final Sync s = new Sync(); + + final Thread thread = newStartedThread(new CheckedRunnable() { + public void realRun() { + try { + if (randomBoolean()) + 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); + } + }