/*
 * 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/
 */

package jsr166;

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 {
    // android-note: Removed because the CTS runner does a bad job of
    // retrying tests that have suite() declarations.
    //
    // 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;
        s = 0L;
        assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L);
        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L);
        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;
        s = 0L;
        assertFalse((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        lock.unlockRead(p);
        assertTrue((s = lock.writeLock()) != 0L);
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
        assertTrue((s = lock.readLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
        assertTrue((s = lock.tryWriteLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
        assertTrue((s = lock.tryReadLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToReadLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockRead(p);
    }

    /**
     * tryConvertToWriteLock succeeds and validates if successfully locked
     * or lock free;
     */
    public void testTryConvertToWriteLock() throws InterruptedException {
        StampedLock lock = new StampedLock();
        long s, p;
        s = 0L;
        assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue((s = lock.tryOptimisticRead()) != 0L);
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        lock.unlockWrite(p);
        assertTrue((s = lock.writeLock()) != 0L);
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockWrite(p);
        assertTrue((s = lock.readLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockWrite(p);
        assertTrue((s = lock.tryWriteLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockWrite(p);
        assertTrue((s = lock.tryReadLock()) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockWrite(p);
        assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L);
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        lock.unlockWrite(p);
        assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L);
        assertTrue(lock.validate(s));
        assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
        assertTrue(lock.validate(p));
        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());
    }

}
