jdk/test/java/util/concurrent/tck/StampedLockTest.java
author dl
Fri, 15 Jul 2016 13:51:43 -0700
changeset 39722 a0b0fa5763b1
parent 35394 282c3cb6a0c1
child 39780 18618975fbb6
permissions -rw-r--r--
8159924: Various improvements to StampedLock code Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 * This file is available under and governed by the GNU General Public
 * License version 2 only, as published by the Free Software Foundation.
 * However, the following notice accompanied the original version of this
 * file:
 *
 * Written by Doug Lea and Martin Buchholz
 * with assistance from members of JCP JSR-166 Expert Group and
 * released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */

import static java.util.concurrent.TimeUnit.MILLISECONDS;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.StampedLock;

import junit.framework.Test;
import junit.framework.TestSuite;

public class StampedLockTest extends JSR166TestCase {
    public static void main(String[] args) {
        main(suite(), args);
    }
    public static Test suite() {
        return new TestSuite(StampedLockTest.class);
    }

    /**
     * A runnable calling writeLockInterruptibly
     */
    class InterruptibleLockRunnable extends CheckedRunnable {
        final StampedLock lock;
        InterruptibleLockRunnable(StampedLock l) { lock = l; }
        public void realRun() throws InterruptedException {
            lock.writeLockInterruptibly();
        }
    }

    /**
     * A runnable calling writeLockInterruptibly that expects to be
     * interrupted
     */
    class InterruptedLockRunnable extends CheckedInterruptedRunnable {
        final StampedLock lock;
        InterruptedLockRunnable(StampedLock l) { lock = l; }
        public void realRun() throws InterruptedException {
            lock.writeLockInterruptibly();
        }
    }

    /**
     * Releases write lock, checking isWriteLocked before and after
     */
    void releaseWriteLock(StampedLock lock, long s) {
        assertTrue(lock.isWriteLocked());
        lock.unlockWrite(s);
        assertFalse(lock.isWriteLocked());
    }

    /**
     * Constructed StampedLock is in unlocked state
     */
    public void testConstructor() {
        StampedLock lock;
        lock = new StampedLock();
        assertFalse(lock.isWriteLocked());
        assertFalse(lock.isReadLocked());
        assertEquals(lock.getReadLockCount(), 0);
    }

    /**
     * write-locking and read-locking an unlocked lock succeed
     */
    public void testLock() {
        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);
    }

    /**
     * tryUnlockRead/Write succeeds if locked in associated mode else
     * returns false
     */
    public void testTryUnlock() {
        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) {}
    }

    /**
     * write-unlocking an unlocked lock throws IllegalMonitorStateException
     */
    public void testWriteUnlock_IMSE2() {
        StampedLock lock = new StampedLock();
        long s = lock.writeLock();
        lock.unlockWrite(s);
        try {
            lock.unlockWrite(s);
            shouldThrow();
        } catch (IllegalMonitorStateException success) {}
    }

    /**
     * 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) {}
    }

    /**
     * read-unlocking an unlocked lock throws IllegalMonitorStateException
     */
    public void testReadUnlock_IMSE() {
        StampedLock lock = new StampedLock();
        long s = lock.readLock();
        lock.unlockRead(s);
        try {
            lock.unlockRead(s);
            shouldThrow();
        } catch (IllegalMonitorStateException success) {}
    }

    /**
     * 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
     */
    public void testValidate0() {
        StampedLock lock = new StampedLock();
        assertFalse(lock.validate(0L));
    }

    /**
     * A stamp obtained from a successful lock operation validates
     */
    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);
    }

    /**
     * A stamp obtained from an unsuccessful lock operation does not validate
     */
    public void testValidate2() throws InterruptedException {
        StampedLock lock = new StampedLock();
        long s;
        assertTrue((s = lock.writeLock()) != 0L);
        assertTrue(lock.validate(s));
        assertFalse(lock.validate(lock.tryWriteLock()));
        assertFalse(lock.validate(lock.tryWriteLock(10L, MILLISECONDS)));
        assertFalse(lock.validate(lock.tryReadLock()));
        assertFalse(lock.validate(lock.tryReadLock(10L, MILLISECONDS)));
        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);
    }

    /**
     * timed tryWriteLock is interruptible
     */
    public void testWriteTryLock_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.tryWriteLock(2 * LONG_DELAY_MS, MILLISECONDS);
            }});

        running.await();
        waitForThreadToEnterWaitState(t, 100);
        t.interrupt();
        awaitTermination(t);
        releaseWriteLock(lock, s);
    }

    /**
     * readLockInterruptibly is interruptible
     */
    public void testReadLockInterruptibly_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.readLockInterruptibly();
            }});

        running.await();
        waitForThreadToEnterWaitState(t, 100);
        t.interrupt();
        awaitTermination(t);
        releaseWriteLock(lock, s);
    }

    /**
     * timed tryReadLock is interruptible
     */
    public void testReadTryLock_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.tryReadLock(2 * LONG_DELAY_MS, MILLISECONDS);
            }});

        running.await();
        waitForThreadToEnterWaitState(t, 100);
        t.interrupt();
        awaitTermination(t);
        releaseWriteLock(lock, s);
    }

    /**
     * tryWriteLock on an unlocked lock succeeds
     */
    public void testWriteTryLock() {
        final StampedLock lock = new StampedLock();
        long s = lock.tryWriteLock();
        assertTrue(s != 0L);
        assertTrue(lock.isWriteLocked());
        long s2 = lock.tryWriteLock();
        assertEquals(s2, 0L);
        releaseWriteLock(lock, s);
    }

    /**
     * tryWriteLock fails if locked
     */
    public void testWriteTryLockWhenLocked() {
        final StampedLock lock = new StampedLock();
        long s = lock.writeLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long ws = lock.tryWriteLock();
                assertTrue(ws == 0L);
            }});

        awaitTermination(t);
        releaseWriteLock(lock, s);
    }

    /**
     * tryReadLock fails if write-locked
     */
    public void testReadTryLockWhenLocked() {
        final StampedLock lock = new StampedLock();
        long s = lock.writeLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long rs = lock.tryReadLock();
                assertEquals(rs, 0L);
            }});

        awaitTermination(t);
        releaseWriteLock(lock, s);
    }

    /**
     * Multiple threads can hold a read lock when not write-locked
     */
    public void testMultipleReadLocks() {
        final StampedLock lock = new StampedLock();
        final long s = lock.readLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() throws InterruptedException {
                long s2 = lock.tryReadLock();
                assertTrue(s2 != 0L);
                lock.unlockRead(s2);
                long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
                assertTrue(s3 != 0L);
                lock.unlockRead(s3);
                long s4 = lock.readLock();
                lock.unlockRead(s4);
            }});

        awaitTermination(t);
        lock.unlockRead(s);
    }

    /**
     * A writelock succeeds only after a reading thread unlocks
     */
    public void testWriteAfterReadLock() throws InterruptedException {
        final CountDownLatch running = new CountDownLatch(1);
        final StampedLock lock = new StampedLock();
        long rs = lock.readLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                running.countDown();
                long s = lock.writeLock();
                lock.unlockWrite(s);
            }});

        running.await();
        waitForThreadToEnterWaitState(t, 100);
        assertFalse(lock.isWriteLocked());
        lock.unlockRead(rs);
        awaitTermination(t);
        assertFalse(lock.isWriteLocked());
    }

    /**
     * A writelock succeeds only after reading threads unlock
     */
    public void testWriteAfterMultipleReadLocks() {
        final StampedLock lock = new StampedLock();
        long s = lock.readLock();
        Thread t1 = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long rs = lock.readLock();
                lock.unlockRead(rs);
            }});

        awaitTermination(t1);

        Thread t2 = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long ws = lock.writeLock();
                lock.unlockWrite(ws);
            }});

        assertFalse(lock.isWriteLocked());
        lock.unlockRead(s);
        awaitTermination(t2);
        assertFalse(lock.isWriteLocked());
    }

    /**
     * Readlocks succeed only after a writing thread unlocks
     */
    public void testReadAfterWriteLock() {
        final StampedLock lock = new StampedLock();
        final long s = lock.writeLock();
        Thread t1 = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long rs = lock.readLock();
                lock.unlockRead(rs);
            }});
        Thread t2 = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long rs = lock.readLock();
                lock.unlockRead(rs);
            }});

        releaseWriteLock(lock, s);
        awaitTermination(t1);
        awaitTermination(t2);
    }

    /**
     * tryReadLock succeeds if readlocked but not writelocked
     */
    public void testTryLockWhenReadLocked() {
        final StampedLock lock = new StampedLock();
        long s = lock.readLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long rs = lock.tryReadLock();
                threadAssertTrue(rs != 0L);
                lock.unlockRead(rs);
            }});

        awaitTermination(t);
        lock.unlockRead(s);
    }

    /**
     * tryWriteLock fails when readlocked
     */
    public void testWriteTryLockWhenReadLocked() {
        final StampedLock lock = new StampedLock();
        long s = lock.readLock();
        Thread t = newStartedThread(new CheckedRunnable() {
            public void realRun() {
                long ws = lock.tryWriteLock();
                threadAssertEquals(ws, 0L);
            }});

        awaitTermination(t);
        lock.unlockRead(s);
    }

    /**
     * timed tryWriteLock times out if locked
     */
    public void testWriteTryLock_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 ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS);
                assertEquals(ws, 0L);
                assertTrue(millisElapsedSince(startTime) >= timeoutMillis);
            }});

        awaitTermination(t);
        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
     */
    public void testReadLockInterruptibly() throws InterruptedException {
        final CountDownLatch running = new CountDownLatch(1);
        final StampedLock lock = new StampedLock();
        long s;
        s = lock.readLockInterruptibly();
        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);
    }

    /**
     * A serialized lock deserializes as unlocked
     */
    public void testSerialization() {
        StampedLock lock = new StampedLock();
        lock.writeLock();
        StampedLock clone = serialClone(lock);
        assertTrue(lock.isWriteLocked());
        assertFalse(clone.isWriteLocked());
        long s = clone.writeLock();
        assertTrue(clone.isWriteLocked());
        clone.unlockWrite(s);
        assertFalse(clone.isWriteLocked());
    }

    /**
     * toString indicates current lock state
     */
    public void testToString() {
        StampedLock lock = new StampedLock();
        assertTrue(lock.toString().contains("Unlocked"));
        long s = lock.writeLock();
        assertTrue(lock.toString().contains("Write-locked"));
        lock.unlockWrite(s);
        s = lock.readLock();
        assertTrue(lock.toString().contains("Read-locks"));
    }

    /**
     * tryOptimisticRead succeeds and validates if unlocked, fails if 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);
    }

    /**
     * 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);
        assertFalse(lock.validate(p));
        assertFalse((p = lock.tryOptimisticRead()) != 0L);
        assertTrue(lock.validate(s));
        lock.unlockWrite(s);
    }

    /**
     * tryOptimisticRead stamp does not validate if a write lock
     * intervenes in another thread
     */
    public void testValidateOptimisticWriteLocked2()
            throws InterruptedException {
        final CountDownLatch running = new CountDownLatch(1);
        final StampedLock lock = new StampedLock();
        long s, p;
        assertTrue((p = lock.tryOptimisticRead()) != 0L);
        Thread t = newStartedThread(new CheckedInterruptedRunnable() {
            public void realRun() throws InterruptedException {
                lock.writeLockInterruptibly();
                running.countDown();
                lock.writeLockInterruptibly();
            }});

        running.await();
        assertFalse(lock.validate(p));
        assertFalse((p = lock.tryOptimisticRead()) != 0L);
        t.interrupt();
        awaitTermination(t);
    }

    /**
     * tryConvertToOptimisticRead succeeds and validates if successfully locked,
     */
    public void testTryConvertToOptimisticRead() throws InterruptedException {
        StampedLock lock = new StampedLock();
        long s, p;
        assertEquals(0L, lock.tryConvertToOptimisticRead(0L));

        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        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));

        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));
    }

    /**
     * tryConvertToReadLock succeeds and validates if successfully locked
     * or lock free;
     */
    public void testTryConvertToReadLock() throws InterruptedException {
        StampedLock lock = new StampedLock();
        long s, p;

        assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L);

        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.isReadLocked());
        assertEquals(1, lock.getReadLockCount());
        lock.unlockRead(p);

        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        lock.readLock();
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        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);

        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);

        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);
    }

    /**
     * tryConvertToWriteLock succeeds and validates if successfully locked
     * or lock free;
     */
    public void testTryConvertToWriteLock() throws InterruptedException {
        StampedLock lock = new StampedLock();
        long s, p;

        assertFalse((p = lock.tryConvertToWriteLock(0L)) != 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);

        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);

        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);
    }

    /**
     * asWriteLock can be locked and unlocked
     */
    public void testAsWriteLock() {
        StampedLock sl = new StampedLock();
        Lock lock = sl.asWriteLock();
        lock.lock();
        assertFalse(lock.tryLock());
        lock.unlock();
        assertTrue(lock.tryLock());
    }

    /**
     * asReadLock can be locked and unlocked
     */
    public void testAsReadLock() {
        StampedLock sl = new StampedLock();
        Lock lock = sl.asReadLock();
        lock.lock();
        lock.unlock();
        assertTrue(lock.tryLock());
    }

    /**
     * asReadWriteLock.writeLock can be locked and unlocked
     */
    public void testAsReadWriteLockWriteLock() {
        StampedLock sl = new StampedLock();
        Lock lock = sl.asReadWriteLock().writeLock();
        lock.lock();
        assertFalse(lock.tryLock());
        lock.unlock();
        assertTrue(lock.tryLock());
    }

    /**
     * asReadWriteLock.readLock can be locked and unlocked
     */
    public void testAsReadWriteLockReadLock() {
        StampedLock sl = new StampedLock();
        Lock lock = sl.asReadWriteLock().readLock();
        lock.lock();
        lock.unlock();
        assertTrue(lock.tryLock());
    }

    /**
     * Lock.newCondition throws UnsupportedOperationException
     */
    public void testLockViewsDoNotSupportConditions() {
        StampedLock sl = new StampedLock();
        assertThrows(UnsupportedOperationException.class,
                     () -> sl.asWriteLock().newCondition(),
                     () -> sl.asReadLock().newCondition(),
                     () -> sl.asReadWriteLock().writeLock().newCondition(),
                     () -> sl.asReadWriteLock().readLock().newCondition());
    }

    /**
     * Passing optimistic read stamps to unlock operations result in
     * IllegalMonitorStateException
     */
    public void testCannotUnlockOptimisticReadStamps() {
        Runnable[] actions = {
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryOptimisticRead();
                assertTrue(stamp != 0);
                sl.unlockRead(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryOptimisticRead();
                sl.unlock(stamp);
            },

            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryOptimisticRead();
                sl.writeLock();
                sl.unlock(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryOptimisticRead();
                sl.readLock();
                sl.unlockRead(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryOptimisticRead();
                sl.readLock();
                sl.unlock(stamp);
            },

            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
                assertTrue(stamp != 0);
                sl.writeLock();
                sl.unlockWrite(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
                sl.writeLock();
                sl.unlock(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
                sl.readLock();
                sl.unlockRead(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.writeLock());
                sl.readLock();
                sl.unlock(stamp);
            },

            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                assertTrue(stamp != 0);
                sl.writeLock();
                sl.unlockWrite(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                sl.writeLock();
                sl.unlock(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                sl.readLock();
                sl.unlockRead(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                sl.readLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                assertTrue(stamp != 0);
                sl.readLock();
                sl.unlockRead(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                sl.readLock();
                sl.unlock(stamp);
            },
            () -> {
                StampedLock sl = new StampedLock();
                sl.readLock();
                long stamp = sl.tryConvertToOptimisticRead(sl.readLock());
                sl.readLock();
                sl.unlock(stamp);
            },
        };

        assertThrows(IllegalMonitorStateException.class, actions);
    }

}