8163210: java/util/concurrent/tck/JSR166TestCase.java testWriteAfterReadLock(StampedLockTest): timed out waiting for thread to terminate
authordl
Mon, 15 Aug 2016 09:09:00 -0700
changeset 40278 8801563939d0
parent 40277 fd3c777aed08
child 40279 7a1e94e544d6
8163210: java/util/concurrent/tck/JSR166TestCase.java testWriteAfterReadLock(StampedLockTest): timed out waiting for thread to terminate Reviewed-by: martin, psandoz, dholmes
jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
jdk/test/java/util/concurrent/tck/JSR166TestCase.java
jdk/test/java/util/concurrent/tck/StampedLockTest.java
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java	Mon Aug 15 09:04:40 2016 -0700
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java	Mon Aug 15 09:09:00 2016 -0700
@@ -263,6 +263,47 @@
      * is theoretically possible, so we additionally add a
      * storeStoreFence after lock acquisition CAS.
      *
+     * ----------------------------------------------------------------
+     * Here's an informal proof that plain reads by _successful_
+     * readers see plain writes from preceding but not following
+     * writers (following Boehm and the C++ standard [atomics.fences]):
+     *
+     * Because of the total synchronization order of accesses to
+     * volatile long state containing the sequence number, writers and
+     * _successful_ readers can be globally sequenced.
+     *
+     * int x, y;
+     *
+     * Writer 1:
+     * inc sequence (odd - "locked")
+     * storeStoreFence();
+     * x = 1; y = 2;
+     * inc sequence (even - "unlocked")
+     *
+     * Successful Reader:
+     * read sequence (even)
+     * // must see writes from Writer 1 but not Writer 2
+     * r1 = x; r2 = y;
+     * acquireFence();
+     * read sequence (even - validated unchanged)
+     * // use r1 and r2
+     *
+     * Writer 2:
+     * inc sequence (odd - "locked")
+     * storeStoreFence();
+     * x = 3; y = 4;
+     * inc sequence (even - "unlocked")
+     *
+     * Visibility of writer 1's stores is normal - reader's initial
+     * read of state synchronizes with writer 1's final write to state.
+     * Lack of visibility of writer 2's plain writes is less obvious.
+     * If reader's read of x or y saw writer 2's write, then (assuming
+     * semantics of C++ fences) the storeStoreFence would "synchronize"
+     * with reader's acquireFence and reader's validation read must see
+     * writer 2's initial write to state and so validation must fail.
+     * But making this "proof" formal and rigorous is an open problem!
+     * ----------------------------------------------------------------
+     *
      * The memory layout keeps lock state and queue pointers together
      * (normally on the same cache line). This usually works well for
      * read-mostly loads. In most other cases, the natural tendency of
@@ -276,14 +317,14 @@
     /** Number of processors, for spin control */
     private static final int NCPU = Runtime.getRuntime().availableProcessors();
 
-    /** Maximum number of retries before enqueuing on acquisition */
-    private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;
+    /** Maximum number of retries before enqueuing on acquisition; at least 1 */
+    private static final int SPINS = (NCPU > 1) ? 1 << 6 : 1;
 
-    /** Maximum number of retries before blocking at head on acquisition */
-    private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;
+    /** Maximum number of tries before blocking at head on acquisition */
+    private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 1;
 
     /** Maximum number of retries before re-blocking */
-    private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;
+    private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 1;
 
     /** The period for yielding when waiting for overflow spinlock */
     private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1
@@ -1228,6 +1269,11 @@
                         WCOWAIT.compareAndSet(h, c, c.cowait) &&
                         (w = c.thread) != null) // help release
                         LockSupport.unpark(w);
+                    if (Thread.interrupted()) {
+                        if (interruptible)
+                            return cancelWaiter(node, p, true);
+                        wasInterrupted = true;
+                    }
                     if (h == (pp = p.prev) || h == p || pp == null) {
                         long m, s, ns;
                         do {
@@ -1264,11 +1310,6 @@
                                 LockSupport.parkNanos(this, time);
                         }
                         node.thread = null;
-                        if (Thread.interrupted()) {
-                            if (interruptible)
-                                return cancelWaiter(node, p, true);
-                            wasInterrupted = true;
-                        }
                     }
                 }
             }
--- a/jdk/test/java/util/concurrent/tck/JSR166TestCase.java	Mon Aug 15 09:04:40 2016 -0700
+++ b/jdk/test/java/util/concurrent/tck/JSR166TestCase.java	Mon Aug 15 09:09:00 2016 -0700
@@ -68,6 +68,7 @@
 import java.security.SecurityPermission;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
 import java.util.Iterator;
@@ -89,6 +90,7 @@
 import java.util.concurrent.Semaphore;
 import java.util.concurrent.SynchronousQueue;
 import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeoutException;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -1278,7 +1280,7 @@
      * thread to enter a wait state: BLOCKED, WAITING, or TIMED_WAITING.
      */
     void waitForThreadToEnterWaitState(Thread thread, long timeoutMillis) {
-        long startTime = System.nanoTime();
+        long startTime = 0L;
         for (;;) {
             Thread.State s = thread.getState();
             if (s == Thread.State.BLOCKED ||
@@ -1287,6 +1289,8 @@
                 return;
             else if (s == Thread.State.TERMINATED)
                 fail("Unexpected thread termination");
+            else if (startTime == 0L)
+                startTime = System.nanoTime();
             else if (millisElapsedSince(startTime) > timeoutMillis) {
                 threadAssertTrue(thread.isAlive());
                 return;
@@ -1900,4 +1904,7 @@
                                1000L, MILLISECONDS,
                                new SynchronousQueue<Runnable>());
 
+    static <T> void shuffle(T[] array) {
+        Collections.shuffle(Arrays.asList(array), ThreadLocalRandom.current());
+    }
 }
--- 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);
     }
-
 }