--- jsr166/src/test/tck/ReentrantReadWriteLockTest.java 2017/01/01 20:34:39 1.79 +++ jsr166/src/test/tck/ReentrantReadWriteLockTest.java 2019/09/26 20:48:53 1.84 @@ -11,16 +11,17 @@ import static java.util.concurrent.TimeU import java.util.Arrays; import java.util.Collection; import java.util.HashSet; +import java.util.concurrent.Callable; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantReadWriteLock; -import junit.framework.AssertionFailedError; import junit.framework.Test; import junit.framework.TestSuite; +@SuppressWarnings("WaitNotInLoop") // we implement spurious-wakeup freedom public class ReentrantReadWriteLockTest extends JSR166TestCase { public static void main(String[] args) { main(suite(), args); @@ -87,7 +88,7 @@ public class ReentrantReadWriteLockTest long startTime = System.nanoTime(); while (!lock.hasQueuedThread(t)) { if (millisElapsedSince(startTime) > LONG_DELAY_MS) - throw new AssertionFailedError("timed out"); + throw new AssertionError("timed out"); Thread.yield(); } assertTrue(t.isAlive()); @@ -1009,42 +1010,41 @@ public class ReentrantReadWriteLockTest public void testAwaitUninterruptibly() { testAwaitUninterruptibly(false); } public void testAwaitUninterruptibly_fair() { testAwaitUninterruptibly(true); } public void testAwaitUninterruptibly(boolean fair) { - final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); - final Condition c = lock.writeLock().newCondition(); + final Lock lock = new ReentrantReadWriteLock(fair).writeLock(); + final Condition condition = lock.newCondition(); final CountDownLatch pleaseInterrupt = new CountDownLatch(2); Thread t1 = newStartedThread(new CheckedRunnable() { public void realRun() { // Interrupt before awaitUninterruptibly - lock.writeLock().lock(); + lock.lock(); pleaseInterrupt.countDown(); Thread.currentThread().interrupt(); - c.awaitUninterruptibly(); + condition.awaitUninterruptibly(); assertTrue(Thread.interrupted()); - lock.writeLock().unlock(); + lock.unlock(); }}); Thread t2 = newStartedThread(new CheckedRunnable() { public void realRun() { // Interrupt during awaitUninterruptibly - lock.writeLock().lock(); + lock.lock(); pleaseInterrupt.countDown(); - c.awaitUninterruptibly(); + condition.awaitUninterruptibly(); assertTrue(Thread.interrupted()); - lock.writeLock().unlock(); + lock.unlock(); }}); await(pleaseInterrupt); - lock.writeLock().lock(); - lock.writeLock().unlock(); t2.interrupt(); - - assertThreadStaysAlive(t1); - assertTrue(t2.isAlive()); - - lock.writeLock().lock(); - c.signalAll(); - lock.writeLock().unlock(); + lock.lock(); + lock.unlock(); + assertThreadBlocks(t1, Thread.State.WAITING); + assertThreadBlocks(t2, Thread.State.WAITING); + + lock.lock(); + condition.signalAll(); + lock.unlock(); awaitTermination(t1); awaitTermination(t2); @@ -1681,4 +1681,64 @@ public class ReentrantReadWriteLockTest assertTrue(lock.writeLock().toString().contains("Unlocked")); } + /** + * ThreadMXBean reports the blockers that we expect. + */ + public void testBlockers() { + if (!testImplementationDetails) return; + final boolean fair = randomBoolean(); + final boolean timedAcquire = randomBoolean(); + final boolean timedAwait = randomBoolean(); + final String syncClassName = fair + ? "ReentrantReadWriteLock$FairSync" + : "ReentrantReadWriteLock$NonfairSync"; + final String conditionClassName + = "AbstractQueuedSynchronizer$ConditionObject"; + final Thread.State expectedAcquireState = timedAcquire + ? Thread.State.TIMED_WAITING + : Thread.State.WAITING; + final Thread.State expectedAwaitState = timedAwait + ? Thread.State.TIMED_WAITING + : Thread.State.WAITING; + final Lock lock = new ReentrantReadWriteLock(fair).writeLock(); + final Condition condition = lock.newCondition(); + final AtomicBoolean conditionSatisfied = new AtomicBoolean(false); + lock.lock(); + final Thread thread = newStartedThread((Action) () -> { + if (timedAcquire) + lock.tryLock(LONGER_DELAY_MS, MILLISECONDS); + else + lock.lock(); + while (!conditionSatisfied.get()) + if (timedAwait) + condition.await(LONGER_DELAY_MS, MILLISECONDS); + else + condition.await(); + }); + Callable waitingForLock = () -> { + String className; + return thread.getState() == expectedAcquireState + && (className = blockerClassName(thread)) != null + && className.endsWith(syncClassName); + }; + waitForThreadToEnterWaitState(thread, waitingForLock); + + lock.unlock(); + Callable waitingForCondition = () -> { + String className; + return thread.getState() == expectedAwaitState + && (className = blockerClassName(thread)) != null + && className.endsWith(conditionClassName); + }; + waitForThreadToEnterWaitState(thread, waitingForCondition); + + // politely release the waiter + conditionSatisfied.set(true); + lock.lock(); + try { + condition.signal(); + } finally { lock.unlock(); } + + awaitTermination(thread); + } }