--- jsr166/src/test/tck/ReentrantReadWriteLockTest.java 2015/07/03 00:25:35 1.76 +++ 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()); @@ -231,7 +232,7 @@ public class ReentrantReadWriteLockTest public void testGetWriteHoldCount() { testGetWriteHoldCount(false); } public void testGetWriteHoldCount_fair() { testGetWriteHoldCount(true); } public void testGetWriteHoldCount(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); for (int i = 1; i <= SIZE; i++) { lock.writeLock().lock(); assertEquals(i,lock.getWriteHoldCount()); @@ -248,7 +249,7 @@ public class ReentrantReadWriteLockTest public void testGetHoldCount() { testGetHoldCount(false); } public void testGetHoldCount_fair() { testGetHoldCount(true); } public void testGetHoldCount(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); for (int i = 1; i <= SIZE; i++) { lock.writeLock().lock(); assertEquals(i,lock.writeLock().getHoldCount()); @@ -265,7 +266,7 @@ public class ReentrantReadWriteLockTest public void testGetReadHoldCount() { testGetReadHoldCount(false); } public void testGetReadHoldCount_fair() { testGetReadHoldCount(true); } public void testGetReadHoldCount(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); for (int i = 1; i <= SIZE; i++) { lock.readLock().lock(); assertEquals(i,lock.getReadHoldCount()); @@ -282,7 +283,7 @@ public class ReentrantReadWriteLockTest public void testWriteUnlock_IMSE() { testWriteUnlock_IMSE(false); } public void testWriteUnlock_IMSE_fair() { testWriteUnlock_IMSE(true); } public void testWriteUnlock_IMSE(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); try { lock.writeLock().unlock(); shouldThrow(); @@ -295,7 +296,7 @@ public class ReentrantReadWriteLockTest public void testReadUnlock_IMSE() { testReadUnlock_IMSE(false); } public void testReadUnlock_IMSE_fair() { testReadUnlock_IMSE(true); } public void testReadUnlock_IMSE(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); try { lock.readLock().unlock(); shouldThrow(); @@ -788,11 +789,11 @@ public class ReentrantReadWriteLockTest public void testWriteTryLock_Timeout(boolean fair) { final PublicReentrantReadWriteLock lock = new PublicReentrantReadWriteLock(fair); + final long timeoutMillis = timeoutMillis(); lock.writeLock().lock(); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { long startTime = System.nanoTime(); - long timeoutMillis = 10; assertFalse(lock.writeLock().tryLock(timeoutMillis, MILLISECONDS)); assertTrue(millisElapsedSince(startTime) >= timeoutMillis); }}); @@ -812,7 +813,7 @@ public class ReentrantReadWriteLockTest Thread t = newStartedThread(new CheckedRunnable() { public void realRun() throws InterruptedException { long startTime = System.nanoTime(); - long timeoutMillis = 10; + long timeoutMillis = timeoutMillis(); assertFalse(lock.readLock().tryLock(timeoutMillis, MILLISECONDS)); assertTrue(millisElapsedSince(startTime) >= timeoutMillis); }}); @@ -924,19 +925,18 @@ public class ReentrantReadWriteLockTest public void testAwaitNanos_Timeout() { testAwaitNanos_Timeout(false); } public void testAwaitNanos_Timeout_fair() { testAwaitNanos_Timeout(true); } public void testAwaitNanos_Timeout(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final long timeoutMillis = timeoutMillis(); + lock.writeLock().lock(); + final long startTime = System.nanoTime(); + final long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); try { - final ReentrantReadWriteLock lock = - new ReentrantReadWriteLock(fair); - final Condition c = lock.writeLock().newCondition(); - lock.writeLock().lock(); - long startTime = System.nanoTime(); - long timeoutMillis = 10; - long timeoutNanos = MILLISECONDS.toNanos(timeoutMillis); long nanosRemaining = c.awaitNanos(timeoutNanos); assertTrue(nanosRemaining <= 0); - assertTrue(millisElapsedSince(startTime) >= timeoutMillis); - lock.writeLock().unlock(); } catch (InterruptedException fail) { threadUnexpectedException(fail); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.writeLock().unlock(); } /** @@ -945,17 +945,16 @@ public class ReentrantReadWriteLockTest public void testAwait_Timeout() { testAwait_Timeout(false); } public void testAwait_Timeout_fair() { testAwait_Timeout(true); } public void testAwait_Timeout(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + final long timeoutMillis = timeoutMillis(); + lock.writeLock().lock(); + final long startTime = System.nanoTime(); try { - final ReentrantReadWriteLock lock = - new ReentrantReadWriteLock(fair); - final Condition c = lock.writeLock().newCondition(); - lock.writeLock().lock(); - long startTime = System.nanoTime(); - long timeoutMillis = 10; assertFalse(c.await(timeoutMillis, MILLISECONDS)); - assertTrue(millisElapsedSince(startTime) >= timeoutMillis); - lock.writeLock().unlock(); } catch (InterruptedException fail) { threadUnexpectedException(fail); } + assertTrue(millisElapsedSince(startTime) >= timeoutMillis); + lock.writeLock().unlock(); } /** @@ -964,18 +963,17 @@ public class ReentrantReadWriteLockTest public void testAwaitUntil_Timeout() { testAwaitUntil_Timeout(false); } public void testAwaitUntil_Timeout_fair() { testAwaitUntil_Timeout(true); } public void testAwaitUntil_Timeout(boolean fair) { + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final Condition c = lock.writeLock().newCondition(); + lock.writeLock().lock(); + // We shouldn't assume that nanoTime and currentTimeMillis + // use the same time source, so don't use nanoTime here. + final java.util.Date delayedDate = delayedDate(timeoutMillis()); try { - final ReentrantReadWriteLock lock = - new ReentrantReadWriteLock(fair); - final Condition c = lock.writeLock().newCondition(); - lock.writeLock().lock(); - long startTime = System.nanoTime(); - long timeoutMillis = 10; - java.util.Date d = new java.util.Date(); - assertFalse(c.awaitUntil(new java.util.Date(d.getTime() + timeoutMillis))); - assertTrue(millisElapsedSince(startTime) >= timeoutMillis); - lock.writeLock().unlock(); + assertFalse(c.awaitUntil(delayedDate)); } catch (InterruptedException fail) { threadUnexpectedException(fail); } + assertTrue(new java.util.Date().getTime() >= delayedDate.getTime()); + lock.writeLock().unlock(); } /** @@ -1012,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); @@ -1231,7 +1228,7 @@ public class ReentrantReadWriteLockTest public void testSerialization() { testSerialization(false); } public void testSerialization_fair() { testSerialization(true); } public void testSerialization(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); lock.writeLock().lock(); lock.readLock().lock(); @@ -1633,14 +1630,20 @@ public class ReentrantReadWriteLockTest public void testToString() { testToString(false); } public void testToString_fair() { testToString(true); } public void testToString(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); assertTrue(lock.toString().contains("Write locks = 0")); assertTrue(lock.toString().contains("Read locks = 0")); lock.writeLock().lock(); assertTrue(lock.toString().contains("Write locks = 1")); assertTrue(lock.toString().contains("Read locks = 0")); + lock.writeLock().lock(); + assertTrue(lock.toString().contains("Write locks = 2")); + assertTrue(lock.toString().contains("Read locks = 0")); + lock.writeLock().unlock(); lock.writeLock().unlock(); lock.readLock().lock(); + assertTrue(lock.toString().contains("Write locks = 0")); + assertTrue(lock.toString().contains("Read locks = 1")); lock.readLock().lock(); assertTrue(lock.toString().contains("Write locks = 0")); assertTrue(lock.toString().contains("Read locks = 2")); @@ -1652,11 +1655,16 @@ public class ReentrantReadWriteLockTest public void testReadLockToString() { testReadLockToString(false); } public void testReadLockToString_fair() { testReadLockToString(true); } public void testReadLockToString(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); assertTrue(lock.readLock().toString().contains("Read locks = 0")); lock.readLock().lock(); + assertTrue(lock.readLock().toString().contains("Read locks = 1")); lock.readLock().lock(); assertTrue(lock.readLock().toString().contains("Read locks = 2")); + lock.readLock().unlock(); + assertTrue(lock.readLock().toString().contains("Read locks = 1")); + lock.readLock().unlock(); + assertTrue(lock.readLock().toString().contains("Read locks = 0")); } /** @@ -1665,12 +1673,72 @@ public class ReentrantReadWriteLockTest public void testWriteLockToString() { testWriteLockToString(false); } public void testWriteLockToString_fair() { testWriteLockToString(true); } public void testWriteLockToString(boolean fair) { - ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(fair); assertTrue(lock.writeLock().toString().contains("Unlocked")); lock.writeLock().lock(); - assertTrue(lock.writeLock().toString().contains("Locked")); + assertTrue(lock.writeLock().toString().contains("Locked by")); lock.writeLock().unlock(); 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); + } }