jdk/test/java/util/concurrent/tck/StampedLockTest.java
changeset 40278 8801563939d0
parent 39780 18618975fbb6
child 40817 4f5fb115676d
--- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java	Mon Aug 15 09:04:40 2016 -0700
+++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java	Mon Aug 15 09:09:00 2016 -0700
@@ -34,10 +34,12 @@
 
 import static java.util.concurrent.TimeUnit.DAYS;
 import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
 
 import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.StampedLock;
@@ -57,207 +59,188 @@
     }
 
     /**
-     * A runnable calling writeLockInterruptibly
+     * Releases write lock, checking isWriteLocked before and after
      */
-    class InterruptibleLockRunnable extends CheckedRunnable {
-        final StampedLock lock;
-        InterruptibleLockRunnable(StampedLock l) { lock = l; }
-        public void realRun() throws InterruptedException {
-            lock.writeLockInterruptibly();
-        }
+    void releaseWriteLock(StampedLock lock, long stamp) {
+        assertTrue(lock.isWriteLocked());
+        assertValid(lock, stamp);
+        lock.unlockWrite(stamp);
+        assertFalse(lock.isWriteLocked());
+        assertFalse(lock.validate(stamp));
     }
 
     /**
-     * A runnable calling writeLockInterruptibly that expects to be
-     * interrupted
+     * Releases read lock, checking isReadLocked before and after
      */
-    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
-        final StampedLock lock;
-        InterruptedLockRunnable(StampedLock l) { lock = l; }
-        public void realRun() throws InterruptedException {
-            lock.writeLockInterruptibly();
-        }
+    void releaseReadLock(StampedLock lock, long stamp) {
+        assertTrue(lock.isReadLocked());
+        assertValid(lock, stamp);
+        lock.unlockRead(stamp);
+        assertFalse(lock.isReadLocked());
+        assertTrue(lock.validate(stamp));
+    }
+
+    long assertNonZero(long v) {
+        assertTrue(v != 0L);
+        return v;
+    }
+
+    long assertValid(StampedLock lock, long stamp) {
+        assertTrue(stamp != 0L);
+        assertTrue(lock.validate(stamp));
+        return stamp;
+    }
+
+    void assertUnlocked(StampedLock lock) {
+        assertFalse(lock.isReadLocked());
+        assertFalse(lock.isWriteLocked());
+        assertEquals(0, lock.getReadLockCount());
+        assertValid(lock, lock.tryOptimisticRead());
+    }
+
+    List<Action> lockLockers(Lock lock) {
+        List<Action> lockers = new ArrayList<>();
+        lockers.add(() -> lock.lock());
+        lockers.add(() -> lock.lockInterruptibly());
+        lockers.add(() -> lock.tryLock());
+        lockers.add(() -> lock.tryLock(Long.MIN_VALUE, DAYS));
+        lockers.add(() -> lock.tryLock(0L, DAYS));
+        lockers.add(() -> lock.tryLock(Long.MAX_VALUE, DAYS));
+        return lockers;
     }
 
-    /**
-     * Releases write lock, checking isWriteLocked before and after
-     */
-    void releaseWriteLock(StampedLock lock, long s) {
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(s);
-        assertFalse(lock.isWriteLocked());
+    List<Function<StampedLock, Long>> readLockers() {
+        List<Function<StampedLock, Long>> readLockers = new ArrayList<>();
+        readLockers.add((sl) -> sl.readLock());
+        readLockers.add((sl) -> sl.tryReadLock());
+        readLockers.add((sl) -> readLockInterruptiblyUninterrupted(sl));
+        readLockers.add((sl) -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+        readLockers.add((sl) -> tryReadLockUninterrupted(sl, 0L, DAYS));
+        readLockers.add((sl) -> sl.tryConvertToReadLock(sl.tryOptimisticRead()));
+        return readLockers;
+    }
+
+    List<BiConsumer<StampedLock, Long>> readUnlockers() {
+        List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>();
+        readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp));
+        readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead()));
+        readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock());
+        readUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+        readUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
+        return readUnlockers;
+    }
+
+    List<Function<StampedLock, Long>> writeLockers() {
+        List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
+        writeLockers.add((sl) -> sl.writeLock());
+        writeLockers.add((sl) -> sl.tryWriteLock());
+        writeLockers.add((sl) -> writeLockInterruptiblyUninterrupted(sl));
+        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
+        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, 0L, DAYS));
+        writeLockers.add((sl) -> sl.tryConvertToWriteLock(sl.tryOptimisticRead()));
+        return writeLockers;
+    }
+
+    List<BiConsumer<StampedLock, Long>> writeUnlockers() {
+        List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
+        writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
+        writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
+        writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
+        writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+        writeUnlockers.add((sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp)));
+        return writeUnlockers;
     }
 
     /**
      * Constructed StampedLock is in unlocked state
      */
     public void testConstructor() {
-        StampedLock lock;
-        lock = new StampedLock();
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
+        assertUnlocked(new StampedLock());
     }
 
     /**
-     * write-locking and read-locking an unlocked lock succeed
+     * write-locking, then unlocking, an unlocked lock succeed
      */
-    public void testLock() {
+    public void testWriteLock_lockUnlock() {
         StampedLock lock = new StampedLock();
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long s = lock.writeLock();
-        assertTrue(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        lock.unlockWrite(s);
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long rs = lock.readLock();
-        assertFalse(lock.isWriteLocked());
-        assertTrue(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 1);
-        lock.unlockRead(rs);
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-    }
 
-    /**
-     * unlock releases either a read or write lock
-     */
-    public void testUnlock() {
-        StampedLock lock = new StampedLock();
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long s = lock.writeLock();
-        assertTrue(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        lock.unlock(s);
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long rs = lock.readLock();
-        assertFalse(lock.isWriteLocked());
-        assertTrue(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 1);
-        lock.unlock(rs);
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            assertFalse(lock.isWriteLocked());
+            assertFalse(lock.isReadLocked());
+            assertEquals(0, lock.getReadLockCount());
+
+            long s = writeLocker.apply(lock);
+            assertValid(lock, s);
+            assertTrue(lock.isWriteLocked());
+            assertFalse(lock.isReadLocked());
+            assertEquals(0, lock.getReadLockCount());
+            writeUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
     }
 
     /**
-     * tryUnlockRead/Write succeeds if locked in associated mode else
-     * returns false
+     * read-locking, then unlocking, an unlocked lock succeed
      */
-    public void testTryUnlock() {
+    public void testReadLock_lockUnlock() {
         StampedLock lock = new StampedLock();
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long s = lock.writeLock();
-        assertTrue(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        assertFalse(lock.tryUnlockRead());
-        assertTrue(lock.tryUnlockWrite());
-        assertFalse(lock.tryUnlockWrite());
-        assertFalse(lock.tryUnlockRead());
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-        long rs = lock.readLock();
-        assertFalse(lock.isWriteLocked());
-        assertTrue(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 1);
-        assertFalse(lock.tryUnlockWrite());
-        assertTrue(lock.tryUnlockRead());
-        assertFalse(lock.tryUnlockRead());
-        assertFalse(lock.tryUnlockWrite());
-        assertFalse(lock.isWriteLocked());
-        assertFalse(lock.isReadLocked());
-        assertEquals(lock.getReadLockCount(), 0);
-    }
 
-    /**
-     * write-unlocking an unlocked lock throws IllegalMonitorStateException
-     */
-    public void testWriteUnlock_IMSE() {
-        StampedLock lock = new StampedLock();
-        try {
-            lock.unlockWrite(0L);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = 42;
+            for (int i = 0; i < 2; i++) {
+                s = assertValid(lock, readLocker.apply(lock));
+                assertFalse(lock.isWriteLocked());
+                assertTrue(lock.isReadLocked());
+                assertEquals(i + 1, lock.getReadLockCount());
+            }
+            for (int i = 0; i < 2; i++) {
+                assertFalse(lock.isWriteLocked());
+                assertTrue(lock.isReadLocked());
+                assertEquals(2 - i, lock.getReadLockCount());
+                readUnlocker.accept(lock, s);
+            }
+            assertUnlocked(lock);
+        }
     }
 
     /**
-     * write-unlocking an unlocked lock throws IllegalMonitorStateException
+     * tryUnlockWrite fails if not write locked
      */
-    public void testWriteUnlock_IMSE2() {
+    public void testTryUnlockWrite_failure() {
         StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        lock.unlockWrite(s);
-        try {
-            lock.unlockWrite(s);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
-    }
+        assertFalse(lock.tryUnlockWrite());
 
-    /**
-     * write-unlocking after readlock throws IllegalMonitorStateException
-     */
-    public void testWriteUnlock_IMSE3() {
-        StampedLock lock = new StampedLock();
-        long s = lock.readLock();
-        try {
-            lock.unlockWrite(s);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertValid(lock, readLocker.apply(lock));
+            assertFalse(lock.tryUnlockWrite());
+            assertTrue(lock.isReadLocked());
+            readUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
     }
 
     /**
-     * read-unlocking an unlocked lock throws IllegalMonitorStateException
+     * tryUnlockRead fails if not read locked
      */
-    public void testReadUnlock_IMSE() {
+    public void testTryUnlockRead_failure() {
         StampedLock lock = new StampedLock();
-        long s = lock.readLock();
-        lock.unlockRead(s);
-        try {
-            lock.unlockRead(s);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
+        assertFalse(lock.tryUnlockRead());
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = writeLocker.apply(lock);
+            assertFalse(lock.tryUnlockRead());
+            assertTrue(lock.isWriteLocked());
+            writeUnlocker.accept(lock, s);
+            assertUnlocked(lock);
+        }
     }
 
     /**
-     * read-unlocking an unlocked lock throws IllegalMonitorStateException
-     */
-    public void testReadUnlock_IMSE2() {
-        StampedLock lock = new StampedLock();
-        try {
-            lock.unlockRead(0L);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
-    }
-
-    /**
-     * read-unlocking after writeLock throws IllegalMonitorStateException
-     */
-    public void testReadUnlock_IMSE3() {
-        StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        try {
-            lock.unlockRead(s);
-            shouldThrow();
-        } catch (IllegalMonitorStateException success) {}
-    }
-
-    /**
-     * validate(0) fails
+     * validate(0L) fails
      */
     public void testValidate0() {
         StampedLock lock = new StampedLock();
@@ -265,29 +248,24 @@
     }
 
     /**
-     * A stamp obtained from a successful lock operation validates
+     * A stamp obtained from a successful lock operation validates while the lock is held
      */
     public void testValidate() throws InterruptedException {
         StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        assertTrue(lock.validate(s));
-        lock.unlockWrite(s);
-        s = lock.readLock();
-        assertTrue(lock.validate(s));
-        lock.unlockRead(s);
-        assertTrue((s = lock.tryWriteLock()) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockWrite(s);
-        assertTrue((s = lock.tryReadLock()) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockRead(s);
-        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockWrite(s);
-        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockRead(s);
-        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertNonZero(readLocker.apply(lock));
+            assertTrue(lock.validate(s));
+            readUnlocker.accept(lock, s);
+        }
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = assertNonZero(writeLocker.apply(lock));
+            assertTrue(lock.validate(s));
+            writeUnlocker.accept(lock, s);
+        }
     }
 
     /**
@@ -295,124 +273,190 @@
      */
     public void testValidate2() throws InterruptedException {
         StampedLock lock = new StampedLock();
-        long s;
-        assertTrue((s = lock.writeLock()) != 0L);
+        long s = assertNonZero(lock.writeLock());
         assertTrue(lock.validate(s));
         assertFalse(lock.validate(lock.tryWriteLock()));
-        assertFalse(lock.validate(lock.tryWriteLock(10L, MILLISECONDS)));
+        assertFalse(lock.validate(lock.tryWriteLock(0L, SECONDS)));
         assertFalse(lock.validate(lock.tryReadLock()));
-        assertFalse(lock.validate(lock.tryReadLock(10L, MILLISECONDS)));
+        assertFalse(lock.validate(lock.tryReadLock(0L, SECONDS)));
         assertFalse(lock.validate(lock.tryOptimisticRead()));
         lock.unlockWrite(s);
     }
 
-    /**
-     * writeLockInterruptibly is interruptible
-     */
-    public void testWriteLockInterruptibly_Interruptible()
-            throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
-        final StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.writeLockInterruptibly();
-            }});
-
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
+    void assertThrowInterruptedExceptionWhenPreInterrupted(Action[] actions) {
+        for (Action action : actions) {
+            Thread.currentThread().interrupt();
+            try {
+                action.run();
+                shouldThrow();
+            }
+            catch (InterruptedException success) {}
+            catch (Throwable fail) { threadUnexpectedException(fail); }
+            assertFalse(Thread.interrupted());
+        }
     }
 
     /**
-     * timed tryWriteLock is interruptible
+     * interruptible operations throw InterruptedException when pre-interrupted
      */
-    public void testWriteTryLock_Interruptible() throws InterruptedException {
+    public void testInterruptibleOperationsThrowInterruptedExceptionWhenPreInterrupted() {
+        final CountDownLatch running = new CountDownLatch(1);
+        final StampedLock lock = new StampedLock();
+
+        Action[] interruptibleLockActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MIN_VALUE, DAYS),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.readLockInterruptibly(),
+            () -> lock.tryReadLock(Long.MIN_VALUE, DAYS),
+            () -> lock.tryReadLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(0L, DAYS),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asReadLock().lockInterruptibly(),
+            () -> lock.asReadLock().tryLock(0L, DAYS),
+            () -> lock.asReadLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockActions);
+
+        assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+        {
+            long s = lock.writeLock();
+            assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+            lock.unlockWrite(s);
+        }
+        {
+            long s = lock.readLock();
+            assertThrowInterruptedExceptionWhenPreInterrupted(interruptibleLockActions);
+            lock.unlockRead(s);
+        }
+    }
+
+    void assertThrowInterruptedExceptionWhenInterrupted(Action[] actions) {
+        int n = actions.length;
+        Future<?>[] futures = new Future<?>[n];
+        CountDownLatch threadsStarted = new CountDownLatch(n);
+        CountDownLatch done = new CountDownLatch(n);
+
+        for (int i = 0; i < n; i++) {
+            Action action = actions[i];
+            futures[i] = cachedThreadPool.submit(new CheckedRunnable() {
+                public void realRun() throws Throwable {
+                    threadsStarted.countDown();
+                    try {
+                        action.run();
+                        shouldThrow();
+                    }
+                    catch (InterruptedException success) {}
+                    catch (Throwable fail) { threadUnexpectedException(fail); }
+                    assertFalse(Thread.interrupted());
+                    done.countDown();
+                }});
+        }
+
+        await(threadsStarted);
+        assertEquals(n, done.getCount());
+        for (Future<?> future : futures) // Interrupt all the tasks
+            future.cancel(true);
+        await(done);
+    }
+
+    /**
+     * interruptible operations throw InterruptedException when write locked and interrupted
+     */
+    public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() {
         final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
         long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.tryWriteLock(2 * LONG_DELAY_MS, MILLISECONDS);
-            }});
 
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
+        Action[] interruptibleLockBlockingActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.readLockInterruptibly(),
+            () -> lock.tryReadLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asReadLock().lockInterruptibly(),
+            () -> lock.asReadLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockBlockingActions);
+
+        assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions);
     }
 
     /**
-     * readLockInterruptibly is interruptible
+     * interruptible operations throw InterruptedException when read locked and interrupted
      */
-    public void testReadLockInterruptibly_Interruptible()
-            throws InterruptedException {
+    public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() {
         final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.readLockInterruptibly();
-            }});
+        long s = lock.readLock();
 
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
+        Action[] interruptibleLockBlockingActions = {
+            () -> lock.writeLockInterruptibly(),
+            () -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
+            () -> lock.asWriteLock().lockInterruptibly(),
+            () -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
+        };
+        shuffle(interruptibleLockBlockingActions);
+
+        assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions);
     }
 
     /**
-     * timed tryReadLock is interruptible
+     * Non-interruptible operations ignore and preserve interrupt status
      */
-    public void testReadTryLock_Interruptible() throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
+    public void testNonInterruptibleOperationsIgnoreInterrupts() {
         final StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.tryReadLock(2 * LONG_DELAY_MS, MILLISECONDS);
-            }});
+        Thread.currentThread().interrupt();
+
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            long s = assertValid(lock, lock.readLock());
+            readUnlocker.accept(lock, s);
+            s = assertValid(lock, lock.tryReadLock());
+            readUnlocker.accept(lock, s);
+        }
+
+        lock.asReadLock().lock();
+        lock.asReadLock().unlock();
 
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            long s = assertValid(lock, lock.writeLock());
+            writeUnlocker.accept(lock, s);
+            s = assertValid(lock, lock.tryWriteLock());
+            writeUnlocker.accept(lock, s);
+        }
+
+        lock.asWriteLock().lock();
+        lock.asWriteLock().unlock();
+
+        assertTrue(Thread.interrupted());
     }
 
     /**
      * tryWriteLock on an unlocked lock succeeds
      */
-    public void testWriteTryLock() {
+    public void testTryWriteLock() {
         final StampedLock lock = new StampedLock();
         long s = lock.tryWriteLock();
         assertTrue(s != 0L);
         assertTrue(lock.isWriteLocked());
-        long s2 = lock.tryWriteLock();
-        assertEquals(s2, 0L);
+        assertEquals(0L, lock.tryWriteLock());
         releaseWriteLock(lock, s);
     }
 
     /**
      * tryWriteLock fails if locked
      */
-    public void testWriteTryLockWhenLocked() {
+    public void testTryWriteLockWhenLocked() {
         final StampedLock lock = new StampedLock();
         long s = lock.writeLock();
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
-                long ws = lock.tryWriteLock();
-                assertTrue(ws == 0L);
+                assertEquals(0L, lock.tryWriteLock());
             }});
 
+        assertEquals(0L, lock.tryWriteLock());
         awaitTermination(t);
         releaseWriteLock(lock, s);
     }
@@ -420,15 +464,15 @@
     /**
      * tryReadLock fails if write-locked
      */
-    public void testReadTryLockWhenLocked() {
+    public void testTryReadLockWhenLocked() {
         final StampedLock lock = new StampedLock();
         long s = lock.writeLock();
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
-                long rs = lock.tryReadLock();
-                assertEquals(rs, 0L);
+                assertEquals(0L, lock.tryReadLock());
             }});
 
+        assertEquals(0L, lock.tryReadLock());
         awaitTermination(t);
         releaseWriteLock(lock, s);
     }
@@ -442,13 +486,20 @@
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() throws InterruptedException {
                 long s2 = lock.tryReadLock();
-                assertTrue(s2 != 0L);
+                assertValid(lock, s2);
                 lock.unlockRead(s2);
                 long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
-                assertTrue(s3 != 0L);
+                assertValid(lock, s3);
                 lock.unlockRead(s3);
                 long s4 = lock.readLock();
+                assertValid(lock, s4);
                 lock.unlockRead(s4);
+                lock.asReadLock().lock();
+                lock.asReadLock().unlock();
+                lock.asReadLock().lockInterruptibly();
+                lock.asReadLock().unlock();
+                lock.asReadLock().tryLock(Long.MIN_VALUE, DAYS);
+                lock.asReadLock().unlock();
             }});
 
         awaitTermination(t);
@@ -470,7 +521,7 @@
             }});
 
         running.await();
-        waitForThreadToEnterWaitState(t, 100);
+        waitForThreadToEnterWaitState(t, MEDIUM_DELAY_MS);
         assertFalse(lock.isWriteLocked());
         lock.unlockRead(rs);
         awaitTermination(t);
@@ -497,6 +548,7 @@
                 lock.unlockWrite(ws);
             }});
 
+        assertTrue(lock.isReadLocked());
         assertFalse(lock.isWriteLocked());
         lock.unlockRead(s);
         awaitTermination(t2);
@@ -508,25 +560,31 @@
      */
     public void testReadAfterWriteLock() {
         final StampedLock lock = new StampedLock();
+        final CountDownLatch threadsStarted = new CountDownLatch(2);
         final long s = lock.writeLock();
         Thread t1 = newStartedThread(new CheckedRunnable() {
             public void realRun() {
+                threadsStarted.countDown();
                 long rs = lock.readLock();
                 lock.unlockRead(rs);
             }});
         Thread t2 = newStartedThread(new CheckedRunnable() {
             public void realRun() {
+                threadsStarted.countDown();
                 long rs = lock.readLock();
                 lock.unlockRead(rs);
             }});
 
+        await(threadsStarted);
+        waitForThreadToEnterWaitState(t1, MEDIUM_DELAY_MS);
+        waitForThreadToEnterWaitState(t2, MEDIUM_DELAY_MS);
         releaseWriteLock(lock, s);
         awaitTermination(t1);
         awaitTermination(t2);
     }
 
     /**
-     * tryReadLock succeeds if readlocked but not writelocked
+     * tryReadLock succeeds if read locked but not write locked
      */
     public void testTryLockWhenReadLocked() {
         final StampedLock lock = new StampedLock();
@@ -534,7 +592,7 @@
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
                 long rs = lock.tryReadLock();
-                threadAssertTrue(rs != 0L);
+                assertValid(lock, rs);
                 lock.unlockRead(rs);
             }});
 
@@ -543,15 +601,14 @@
     }
 
     /**
-     * tryWriteLock fails when readlocked
+     * tryWriteLock fails when read locked
      */
-    public void testWriteTryLockWhenReadLocked() {
+    public void testTryWriteLockWhenReadLocked() {
         final StampedLock lock = new StampedLock();
         long s = lock.readLock();
         Thread t = newStartedThread(new CheckedRunnable() {
             public void realRun() {
-                long ws = lock.tryWriteLock();
-                threadAssertEquals(ws, 0L);
+                threadAssertEquals(0L, lock.tryWriteLock());
             }});
 
         awaitTermination(t);
@@ -559,86 +616,82 @@
     }
 
     /**
-     * timed tryWriteLock times out if locked
+     * timed lock operations time out if lock not available
      */
-    public void testWriteTryLock_Timeout() {
+    public void testTimedLock_Timeout() throws Exception {
+        ArrayList<Future<?>> futures = new ArrayList<>();
+
+        // Write locked
         final StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedRunnable() {
+        long stamp = lock.writeLock();
+        assertEquals(0L, lock.tryReadLock(0L, DAYS));
+        assertEquals(0L, lock.tryReadLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock.asReadLock().tryLock(0L, DAYS));
+        assertFalse(lock.asReadLock().tryLock(Long.MIN_VALUE, DAYS));
+        assertEquals(0L, lock.tryWriteLock(0L, DAYS));
+        assertEquals(0L, lock.tryWriteLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock.asWriteLock().tryLock(0L, DAYS));
+        assertFalse(lock.asWriteLock().tryLock(Long.MIN_VALUE, DAYS));
+
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertEquals(0L, lock.tryWriteLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
             public void realRun() throws InterruptedException {
                 long startTime = System.nanoTime();
-                long timeoutMillis = 10;
-                long ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS);
-                assertEquals(ws, 0L);
-                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
-            }});
+                assertEquals(0L, lock.tryReadLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        // Read locked
+        final StampedLock lock2 = new StampedLock();
+        long stamp2 = lock2.readLock();
+        assertEquals(0L, lock2.tryWriteLock(0L, DAYS));
+        assertEquals(0L, lock2.tryWriteLock(Long.MIN_VALUE, DAYS));
+        assertFalse(lock2.asWriteLock().tryLock(0L, DAYS));
+        assertFalse(lock2.asWriteLock().tryLock(Long.MIN_VALUE, DAYS));
 
-        awaitTermination(t);
+        futures.add(cachedThreadPool.submit(new CheckedRunnable() {
+            public void realRun() throws InterruptedException {
+                long startTime = System.nanoTime();
+                assertEquals(0L, lock2.tryWriteLock(timeoutMillis(), MILLISECONDS));
+                assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
+            }}));
+
+        for (Future<?> future : futures)
+            assertNull(future.get());
+
+        releaseWriteLock(lock, stamp);
+        releaseReadLock(lock2, stamp2);
+    }
+
+    /**
+     * writeLockInterruptibly succeeds if unlocked
+     */
+    public void testWriteLockInterruptibly() throws InterruptedException {
+        final StampedLock lock = new StampedLock();
+        long s = lock.writeLockInterruptibly();
+        assertTrue(lock.isWriteLocked());
         releaseWriteLock(lock, s);
     }
 
     /**
-     * timed tryReadLock times out if write-locked
-     */
-    public void testReadTryLock_Timeout() {
-        final StampedLock lock = new StampedLock();
-        long s = lock.writeLock();
-        Thread t = newStartedThread(new CheckedRunnable() {
-            public void realRun() throws InterruptedException {
-                long startTime = System.nanoTime();
-                long timeoutMillis = 10;
-                long rs = lock.tryReadLock(timeoutMillis, MILLISECONDS);
-                assertEquals(rs, 0L);
-                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
-            }});
-
-        awaitTermination(t);
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(s);
-    }
-
-    /**
-     * writeLockInterruptibly succeeds if unlocked, else is interruptible
-     */
-    public void testWriteLockInterruptibly() throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
-        final StampedLock lock = new StampedLock();
-        long s = lock.writeLockInterruptibly();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.writeLockInterruptibly();
-            }});
-
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        assertTrue(lock.isWriteLocked());
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
-    }
-
-    /**
-     * readLockInterruptibly succeeds if lock free else is interruptible
+     * readLockInterruptibly succeeds if lock free
      */
     public void testReadLockInterruptibly() throws InterruptedException {
-        final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
-        long s;
-        s = lock.readLockInterruptibly();
+
+        long s = assertValid(lock, lock.readLockInterruptibly());
+        assertTrue(lock.isReadLocked());
         lock.unlockRead(s);
-        s = lock.writeLockInterruptibly();
-        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
-            public void realRun() throws InterruptedException {
-                running.countDown();
-                lock.readLockInterruptibly();
-            }});
 
-        running.await();
-        waitForThreadToEnterWaitState(t, 100);
-        t.interrupt();
-        awaitTermination(t);
-        releaseWriteLock(lock, s);
+        lock.asReadLock().lockInterruptibly();
+        assertTrue(lock.isReadLocked());
+        lock.asReadLock().unlock();
     }
 
     /**
@@ -670,54 +723,39 @@
     }
 
     /**
-     * tryOptimisticRead succeeds and validates if unlocked, fails if locked
+     * tryOptimisticRead succeeds and validates if unlocked, fails if
+     * exclusively locked
      */
     public void testValidateOptimistic() throws InterruptedException {
         StampedLock lock = new StampedLock();
-        long s, p;
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue((s = lock.writeLock()) != 0L);
-        assertFalse((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockWrite(s);
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue((s = lock.readLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue(lock.validate(p));
-        lock.unlockRead(s);
-        assertTrue((s = lock.tryWriteLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertFalse((p = lock.tryOptimisticRead()) != 0L);
-        lock.unlockWrite(s);
-        assertTrue((s = lock.tryReadLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        lock.unlockRead(s);
-        assertTrue(lock.validate(p));
-        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
-        assertFalse((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue(lock.validate(s));
-        lock.unlockWrite(s);
-        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        lock.unlockRead(s);
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+
+        assertValid(lock, lock.tryOptimisticRead());
+
+        for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+            long s = assertValid(lock, writeLocker.apply(lock));
+            assertEquals(0L, lock.tryOptimisticRead());
+            releaseWriteLock(lock, s);
+        }
+
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            long s = assertValid(lock, readLocker.apply(lock));
+            long p = assertValid(lock, lock.tryOptimisticRead());
+            releaseReadLock(lock, s);
+            assertTrue(lock.validate(p));
+        }
+
+        assertValid(lock, lock.tryOptimisticRead());
     }
 
     /**
      * tryOptimisticRead stamp does not validate if a write lock intervenes
      */
     public void testValidateOptimisticWriteLocked() {
-        StampedLock lock = new StampedLock();
-        long s, p;
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
-        assertTrue((s = lock.writeLock()) != 0L);
+        final StampedLock lock = new StampedLock();
+        final long p = assertValid(lock, lock.tryOptimisticRead());
+        final long s = assertValid(lock, lock.writeLock());
         assertFalse(lock.validate(p));
-        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        assertEquals(0L, lock.tryOptimisticRead());
         assertTrue(lock.validate(s));
         lock.unlockWrite(s);
     }
@@ -730,8 +768,8 @@
             throws InterruptedException {
         final CountDownLatch running = new CountDownLatch(1);
         final StampedLock lock = new StampedLock();
-        long s, p;
-        assertTrue((p = lock.tryOptimisticRead()) != 0L);
+        final long p = assertValid(lock, lock.tryOptimisticRead());
+
         Thread t = newStartedThread(new CheckedInterruptedRunnable() {
             public void realRun() throws InterruptedException {
                 lock.writeLockInterruptibly();
@@ -741,228 +779,209 @@
 
         running.await();
         assertFalse(lock.validate(p));
-        assertFalse((p = lock.tryOptimisticRead()) != 0L);
+        assertEquals(0L, lock.tryOptimisticRead());
         t.interrupt();
         awaitTermination(t);
     }
 
     /**
-     * tryConvertToOptimisticRead succeeds and validates if successfully locked,
+     * tryConvertToOptimisticRead succeeds and validates if successfully locked
      */
     public void testTryConvertToOptimisticRead() throws InterruptedException {
         StampedLock lock = new StampedLock();
-        long s, p;
+        long s, p, q;
         assertEquals(0L, lock.tryConvertToOptimisticRead(0L));
 
-        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        s = assertValid(lock, lock.tryOptimisticRead());
         assertEquals(s, lock.tryConvertToOptimisticRead(s));
         assertTrue(lock.validate(s));
 
-        assertTrue((p = lock.readLock()) != 0L);
-        assertTrue((s = lock.tryOptimisticRead()) != 0L);
-        assertEquals(s, lock.tryConvertToOptimisticRead(s));
-        assertTrue(lock.validate(s));
-        lock.unlockRead(p);
-
-        assertTrue((s = lock.writeLock()) != 0L);
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
-
-        assertTrue((s = lock.readLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
+        for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+            s = assertValid(lock, writeLocker.apply(lock));
+            p = assertValid(lock, lock.tryConvertToOptimisticRead(s));
+            assertFalse(lock.validate(s));
+            assertTrue(lock.validate(p));
+            assertUnlocked(lock);
+        }
 
-        assertTrue((s = lock.tryWriteLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
-
-        assertTrue((s = lock.tryReadLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
-
-        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
-
-        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
-        assertTrue(lock.validate(p));
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            s = assertValid(lock, readLocker.apply(lock));
+            q = assertValid(lock, lock.tryOptimisticRead());
+            assertEquals(q, lock.tryConvertToOptimisticRead(q));
+            assertTrue(lock.validate(q));
+            assertTrue(lock.isReadLocked());
+            p = assertValid(lock, lock.tryConvertToOptimisticRead(s));
+            assertTrue(lock.validate(p));
+            assertTrue(lock.validate(s));
+            assertUnlocked(lock);
+            assertEquals(q, lock.tryConvertToOptimisticRead(q));
+            assertTrue(lock.validate(q));
+        }
     }
 
     /**
-     * tryConvertToReadLock succeeds and validates if successfully locked
-     * or lock free;
+     * tryConvertToReadLock succeeds for valid stamps
      */
     public void testTryConvertToReadLock() throws InterruptedException {
         StampedLock lock = new StampedLock();
         long s, p;
 
-        assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L);
+        assertEquals(0L, lock.tryConvertToReadLock(0L));
 
-        assertTrue((s = lock.tryOptimisticRead()) != 0L);
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        s = assertValid(lock, lock.tryOptimisticRead());
+        p = assertValid(lock, lock.tryConvertToReadLock(s));
         assertTrue(lock.isReadLocked());
         assertEquals(1, lock.getReadLockCount());
+        assertTrue(lock.validate(s));
         lock.unlockRead(p);
 
-        assertTrue((s = lock.tryOptimisticRead()) != 0L);
+        s = assertValid(lock, lock.tryOptimisticRead());
         lock.readLock();
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
+        p = assertValid(lock, lock.tryConvertToReadLock(s));
         assertTrue(lock.isReadLocked());
         assertEquals(2, lock.getReadLockCount());
         lock.unlockRead(p);
         lock.unlockRead(p);
-
-        assertTrue((s = lock.writeLock()) != 0L);
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isReadLocked());
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(p);
-
-        assertTrue((s = lock.readLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertEquals(s, lock.tryConvertToReadLock(s));
-        assertTrue(lock.validate(s));
-        assertTrue(lock.isReadLocked());
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(s);
-
-        assertTrue((s = lock.tryWriteLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(p);
+        assertUnlocked(lock);
 
-        assertTrue((s = lock.tryReadLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertEquals(s, lock.tryConvertToReadLock(s));
-        assertTrue(lock.validate(s));
-        assertTrue(lock.isReadLocked());
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(s);
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+                s = assertValid(lock, writeLocker.apply(lock));
+                p = assertValid(lock, lock.tryConvertToReadLock(s));
+                assertFalse(lock.validate(s));
+                assertTrue(lock.isReadLocked());
+                assertEquals(1, lock.getReadLockCount());
+                readUnlocker.accept(lock, p);
+            }
 
-        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
-        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isReadLocked());
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(p);
-
-        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        assertEquals(s, lock.tryConvertToReadLock(s));
-        assertTrue(lock.validate(s));
-        assertTrue(lock.isReadLocked());
-        assertEquals(1, lock.getReadLockCount());
-        lock.unlockRead(s);
+            for (Function<StampedLock, Long> readLocker : readLockers()) {
+                s = assertValid(lock, readLocker.apply(lock));
+                assertEquals(s, lock.tryConvertToReadLock(s));
+                assertTrue(lock.validate(s));
+                assertTrue(lock.isReadLocked());
+                assertEquals(1, lock.getReadLockCount());
+                readUnlocker.accept(lock, s);
+            }
+        }
     }
 
     /**
-     * tryConvertToWriteLock succeeds and validates if successfully locked
-     * or lock free;
+     * tryConvertToWriteLock succeeds if lock available; fails if multiply read locked
      */
     public void testTryConvertToWriteLock() throws InterruptedException {
         StampedLock lock = new StampedLock();
         long s, p;
 
-        assertFalse((p = lock.tryConvertToWriteLock(0L)) != 0L);
+        assertEquals(0L, lock.tryConvertToWriteLock(0L));
 
         assertTrue((s = lock.tryOptimisticRead()) != 0L);
         assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
         assertTrue(lock.isWriteLocked());
         lock.unlockWrite(p);
 
-        assertTrue((s = lock.writeLock()) != 0L);
-        assertEquals(s, lock.tryConvertToWriteLock(s));
-        assertTrue(lock.validate(s));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(s);
-
-        assertTrue((s = lock.readLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(p);
-
-        assertTrue((s = lock.tryWriteLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertEquals(s, lock.tryConvertToWriteLock(s));
-        assertTrue(lock.validate(s));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(s);
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
+            for (Function<StampedLock, Long> writeLocker : writeLockers()) {
+                s = assertValid(lock, writeLocker.apply(lock));
+                assertEquals(s, lock.tryConvertToWriteLock(s));
+                assertTrue(lock.validate(s));
+                assertTrue(lock.isWriteLocked());
+                writeUnlocker.accept(lock, s);
+            }
 
-        assertTrue((s = lock.tryReadLock()) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(p);
+            for (Function<StampedLock, Long> readLocker : readLockers()) {
+                s = assertValid(lock, readLocker.apply(lock));
+                p = assertValid(lock, lock.tryConvertToWriteLock(s));
+                assertFalse(lock.validate(s));
+                assertTrue(lock.validate(p));
+                assertTrue(lock.isWriteLocked());
+                writeUnlocker.accept(lock, p);
+            }
+        }
 
-        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(p);
-
-        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
-        assertTrue(lock.validate(s));
-        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
-        assertTrue(lock.validate(p));
-        assertTrue(lock.isWriteLocked());
-        lock.unlockWrite(p);
+        // failure if multiply read locked
+        for (Function<StampedLock, Long> readLocker : readLockers()) {
+            s = assertValid(lock, readLocker.apply(lock));
+            p = assertValid(lock, readLocker.apply(lock));
+            assertEquals(0L, lock.tryConvertToWriteLock(s));
+            assertTrue(lock.validate(s));
+            assertTrue(lock.validate(p));
+            assertEquals(2, lock.getReadLockCount());
+            lock.unlock(p);
+            lock.unlock(s);
+            assertUnlocked(lock);
+        }
     }
 
     /**
      * asWriteLock can be locked and unlocked
      */
-    public void testAsWriteLock() {
+    public void testAsWriteLock() throws Throwable {
         StampedLock sl = new StampedLock();
         Lock lock = sl.asWriteLock();
-        lock.lock();
-        assertFalse(lock.tryLock());
-        lock.unlock();
-        assertTrue(lock.tryLock());
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isWriteLocked());
+            assertFalse(sl.isReadLocked());
+            assertFalse(lock.tryLock());
+            lock.unlock();
+            assertUnlocked(sl);
+        }
     }
 
     /**
      * asReadLock can be locked and unlocked
      */
-    public void testAsReadLock() {
+    public void testAsReadLock() throws Throwable {
         StampedLock sl = new StampedLock();
         Lock lock = sl.asReadLock();
-        lock.lock();
-        lock.unlock();
-        assertTrue(lock.tryLock());
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertFalse(sl.isWriteLocked());
+            assertEquals(1, sl.getReadLockCount());
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertEquals(2, sl.getReadLockCount());
+            lock.unlock();
+            lock.unlock();
+            assertUnlocked(sl);
+        }
     }
 
     /**
      * asReadWriteLock.writeLock can be locked and unlocked
      */
-    public void testAsReadWriteLockWriteLock() {
+    public void testAsReadWriteLockWriteLock() throws Throwable {
         StampedLock sl = new StampedLock();
         Lock lock = sl.asReadWriteLock().writeLock();
-        lock.lock();
-        assertFalse(lock.tryLock());
-        lock.unlock();
-        assertTrue(lock.tryLock());
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isWriteLocked());
+            assertFalse(sl.isReadLocked());
+            assertFalse(lock.tryLock());
+            lock.unlock();
+            assertUnlocked(sl);
+        }
     }
 
     /**
      * asReadWriteLock.readLock can be locked and unlocked
      */
-    public void testAsReadWriteLockReadLock() {
+    public void testAsReadWriteLockReadLock() throws Throwable {
         StampedLock sl = new StampedLock();
         Lock lock = sl.asReadWriteLock().readLock();
-        lock.lock();
-        lock.unlock();
-        assertTrue(lock.tryLock());
+        for (Action locker : lockLockers(lock)) {
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertFalse(sl.isWriteLocked());
+            assertEquals(1, sl.getReadLockCount());
+            locker.run();
+            assertTrue(sl.isReadLocked());
+            assertEquals(2, sl.getReadLockCount());
+            lock.unlock();
+            lock.unlock();
+            assertUnlocked(sl);
+        }
     }
 
     /**
@@ -985,8 +1004,7 @@
         Runnable[] actions = {
             () -> {
                 StampedLock sl = new StampedLock();
-                long stamp = sl.tryOptimisticRead();
-                assertTrue(stamp != 0);
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
                 sl.unlockRead(stamp);
             },
             () -> {
@@ -1003,21 +1021,21 @@
             },
             () -> {
                 StampedLock sl = new StampedLock();
-                long stamp = sl.tryOptimisticRead();
                 sl.readLock();
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
                 sl.unlockRead(stamp);
             },
             () -> {
                 StampedLock sl = new StampedLock();
-                long stamp = sl.tryOptimisticRead();
                 sl.readLock();
+                long stamp = assertValid(sl, sl.tryOptimisticRead());
                 sl.unlock(stamp);
             },
 
             () -> {
                 StampedLock sl = new StampedLock();
                 long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
-                assertTrue(stamp != 0);
+                assertValid(sl, stamp);
                 sl.writeLock();
                 sl.unlockWrite(stamp);
             },
@@ -1043,7 +1061,7 @@
             () -> {
                 StampedLock sl = new StampedLock();
                 long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
-                assertTrue(stamp != 0);
+                assertValid(sl, stamp);
                 sl.writeLock();
                 sl.unlockWrite(stamp);
             },
@@ -1063,7 +1081,7 @@
                 StampedLock sl = new StampedLock();
                 sl.readLock();
                 long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
-                assertTrue(stamp != 0);
+                assertValid(sl, stamp);
                 sl.readLock();
                 sl.unlockRead(stamp);
             },
@@ -1106,100 +1124,84 @@
     }
 
     /**
-     * Invalid write stamps result in IllegalMonitorStateException
+     * Invalid stamps result in IllegalMonitorStateException
      */
-    public void testInvalidWriteStampsThrowIllegalMonitorStateException() {
-        List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
-        writeLockers.add((sl) -> sl.writeLock());
-        writeLockers.add((sl) -> writeLockInterruptiblyUninterrupted(sl));
-        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
-        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, 0, DAYS));
+    public void testInvalidStampsThrowIllegalMonitorStateException() {
+        final StampedLock sl = new StampedLock();
+
+        assertThrows(IllegalMonitorStateException.class,
+                     () -> sl.unlockWrite(0L),
+                     () -> sl.unlockRead(0L),
+                     () -> sl.unlock(0L));
 
-        List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
-        writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
-        writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
-        writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
-        writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
+        final long optimisticStamp = sl.tryOptimisticRead();
+        final long readStamp = sl.readLock();
+        sl.unlockRead(readStamp);
+        final long writeStamp = sl.writeLock();
+        sl.unlockWrite(writeStamp);
+        assertTrue(optimisticStamp != 0L && readStamp != 0L && writeStamp != 0L);
+        final long[] noLongerValidStamps = { optimisticStamp, readStamp, writeStamp };
+        final Runnable assertNoLongerValidStampsThrow = () -> {
+            for (long noLongerValidStamp : noLongerValidStamps)
+                assertThrows(IllegalMonitorStateException.class,
+                             () -> sl.unlockWrite(noLongerValidStamp),
+                             () -> sl.unlockRead(noLongerValidStamp),
+                             () -> sl.unlock(noLongerValidStamp));
+        };
+        assertNoLongerValidStampsThrow.run();
 
-        List<Consumer<StampedLock>> mutaters = new ArrayList<>();
-        mutaters.add((sl) -> {});
-        mutaters.add((sl) -> sl.readLock());
-        for (Function<StampedLock, Long> writeLocker : writeLockers)
-            mutaters.add((sl) -> writeLocker.apply(sl));
+        for (Function<StampedLock, Long> readLocker : readLockers())
+        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
+            final long stamp = readLocker.apply(sl);
+            assertValid(sl, stamp);
+            assertNoLongerValidStampsThrow.run();
+            assertThrows(IllegalMonitorStateException.class,
+                         () -> sl.unlockWrite(stamp),
+                         () -> sl.unlockRead(sl.tryOptimisticRead()),
+                         () -> sl.unlockRead(0L));
+            readUnlocker.accept(sl, stamp);
+            assertUnlocked(sl);
+            assertNoLongerValidStampsThrow.run();
+        }
 
-        for (Function<StampedLock, Long> writeLocker : writeLockers)
-        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers)
-        for (Consumer<StampedLock> mutater : mutaters) {
-            final StampedLock sl = new StampedLock();
+        for (Function<StampedLock, Long> writeLocker : writeLockers())
+        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
             final long stamp = writeLocker.apply(sl);
-            assertTrue(stamp != 0L);
+            assertValid(sl, stamp);
+            assertNoLongerValidStampsThrow.run();
             assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlockRead(stamp));
+                         () -> sl.unlockRead(stamp),
+                         () -> sl.unlockWrite(0L));
             writeUnlocker.accept(sl, stamp);
-            mutater.accept(sl);
-            assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlock(stamp),
-                         () -> sl.unlockRead(stamp),
-                         () -> sl.unlockWrite(stamp));
+            assertUnlocked(sl);
+            assertNoLongerValidStampsThrow.run();
         }
     }
 
     /**
-     * Invalid read stamps result in IllegalMonitorStateException
+     * Read locks can be very deeply nested
      */
-    public void testInvalidReadStampsThrowIllegalMonitorStateException() {
-        List<Function<StampedLock, Long>> readLockers = new ArrayList<>();
-        readLockers.add((sl) -> sl.readLock());
-        readLockers.add((sl) -> readLockInterruptiblyUninterrupted(sl));
-        readLockers.add((sl) -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
-        readLockers.add((sl) -> tryReadLockUninterrupted(sl, 0, DAYS));
-
-        List<BiConsumer<StampedLock, Long>> readUnlockers = new ArrayList<>();
-        readUnlockers.add((sl, stamp) -> sl.unlockRead(stamp));
-        readUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockRead()));
-        readUnlockers.add((sl, stamp) -> sl.asReadLock().unlock());
-        readUnlockers.add((sl, stamp) -> sl.unlock(stamp));
-
-        List<Function<StampedLock, Long>> writeLockers = new ArrayList<>();
-        writeLockers.add((sl) -> sl.writeLock());
-        writeLockers.add((sl) -> writeLockInterruptiblyUninterrupted(sl));
-        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS));
-        writeLockers.add((sl) -> tryWriteLockUninterrupted(sl, 0, DAYS));
-
-        List<BiConsumer<StampedLock, Long>> writeUnlockers = new ArrayList<>();
-        writeUnlockers.add((sl, stamp) -> sl.unlockWrite(stamp));
-        writeUnlockers.add((sl, stamp) -> assertTrue(sl.tryUnlockWrite()));
-        writeUnlockers.add((sl, stamp) -> sl.asWriteLock().unlock());
-        writeUnlockers.add((sl, stamp) -> sl.unlock(stamp));
-
-
-        for (Function<StampedLock, Long> readLocker : readLockers)
-        for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers)
-        for (Function<StampedLock, Long> writeLocker : writeLockers)
-        for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers) {
-            final StampedLock sl = new StampedLock();
-            final long stamp = readLocker.apply(sl);
-            assertTrue(stamp != 0L);
-            assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlockWrite(stamp));
-            readUnlocker.accept(sl, stamp);
-            assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlock(stamp),
-                         () -> sl.unlockRead(stamp),
-                         () -> sl.unlockWrite(stamp));
-            final long writeStamp = writeLocker.apply(sl);
-            assertTrue(writeStamp != 0L);
-            assertTrue(writeStamp != stamp);
-            assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlock(stamp),
-                         () -> sl.unlockRead(stamp),
-                         () -> sl.unlockWrite(stamp));
-            writeUnlocker.accept(sl, writeStamp);
-            assertThrows(IllegalMonitorStateException.class,
-                         () -> sl.unlock(stamp),
-                         () -> sl.unlockRead(stamp),
-                         () -> sl.unlockWrite(stamp));
+    public void testDeeplyNestedReadLocks() {
+        final StampedLock lock = new StampedLock();
+        final int depth = 300;
+        final long[] stamps = new long[depth];
+        final List<Function<StampedLock, Long>> readLockers = readLockers();
+        final List<BiConsumer<StampedLock, Long>> readUnlockers = readUnlockers();
+        for (int i = 0; i < depth; i++) {
+            Function<StampedLock, Long> readLocker
+                = readLockers.get(i % readLockers.size());
+            long stamp = readLocker.apply(lock);
+            assertEquals(i + 1, lock.getReadLockCount());
+            assertTrue(lock.isReadLocked());
+            stamps[i] = stamp;
         }
+        for (int i = 0; i < depth; i++) {
+            BiConsumer<StampedLock, Long> readUnlocker
+                = readUnlockers.get(i % readUnlockers.size());
+            assertEquals(depth - i, lock.getReadLockCount());
+            assertTrue(lock.isReadLocked());
+            readUnlocker.accept(lock, stamps[depth - 1 - i]);
+        }
+        assertUnlocked(lock);
     }
-
 }