--- jsr166/src/test/tck/StampedLockTest.java 2015/05/15 18:21:19 1.14 +++ jsr166/src/test/tck/StampedLockTest.java 2017/03/25 21:41:10 1.34 @@ -5,11 +5,19 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ +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; +import java.util.function.BiConsumer; +import java.util.function.Function; import junit.framework.Test; import junit.framework.TestSuite; @@ -23,207 +31,188 @@ public class StampedLockTest extends JSR } /** - * 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) { + void releaseWriteLock(StampedLock lock, long stamp) { assertTrue(lock.isWriteLocked()); - lock.unlockWrite(s); + assertValid(lock, stamp); + lock.unlockWrite(stamp); assertFalse(lock.isWriteLocked()); + assertFalse(lock.validate(stamp)); } /** - * Constructed StampedLock is in unlocked state + * Releases read lock, checking isReadLocked before and after */ - public void testConstructor() { - StampedLock lock; - lock = new StampedLock(); - assertFalse(lock.isWriteLocked()); + void releaseReadLock(StampedLock lock, long stamp) { + assertTrue(lock.isReadLocked()); + assertValid(lock, stamp); + lock.unlockRead(stamp); assertFalse(lock.isReadLocked()); - assertEquals(lock.getReadLockCount(), 0); + assertTrue(lock.validate(stamp)); } - /** - * 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); + long assertNonZero(long v) { + assertTrue(v != 0L); + return v; } - /** - * 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); + long assertValid(StampedLock lock, long stamp) { + assertTrue(stamp != 0L); + assertTrue(lock.validate(stamp)); + return stamp; } - /** - * 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()); + void assertUnlocked(StampedLock lock) { 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); + assertEquals(0, lock.getReadLockCount()); + assertValid(lock, lock.tryOptimisticRead()); } - /** - * write-unlocking an unlocked lock throws IllegalMonitorStateException - */ - public void testWriteUnlock_IMSE() { - StampedLock lock = new StampedLock(); - try { - lock.unlockWrite(0L); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + List lockLockers(Lock lock) { + List 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; + } + + List> readLockers() { + List> 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> readUnlockers() { + List> 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> writeLockers() { + List> 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> writeUnlockers() { + List> 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; } /** - * write-unlocking an unlocked lock throws IllegalMonitorStateException + * Constructed StampedLock is in unlocked state */ - public void testWriteUnlock_IMSE2() { - StampedLock lock = new StampedLock(); - long s = lock.writeLock(); - lock.unlockWrite(s); - try { - lock.unlockWrite(s); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + public void testConstructor() { + assertUnlocked(new StampedLock()); } /** - * write-unlocking after readlock throws IllegalMonitorStateException + * write-locking, then unlocking, an unlocked lock succeed */ - public void testWriteUnlock_IMSE3() { + public void testWriteLock_lockUnlock() { StampedLock lock = new StampedLock(); - long s = lock.readLock(); - try { - lock.unlockWrite(s); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + + for (Function writeLocker : writeLockers()) + for (BiConsumer 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); + } } /** - * read-unlocking an unlocked lock throws IllegalMonitorStateException + * read-locking, then unlocking, an unlocked lock succeed */ - public void testReadUnlock_IMSE() { + public void testReadLock_lockUnlock() { StampedLock lock = new StampedLock(); - long s = lock.readLock(); - lock.unlockRead(s); - try { - lock.unlockRead(s); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + + for (Function readLocker : readLockers()) + for (BiConsumer 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); + } } /** - * read-unlocking an unlocked lock throws IllegalMonitorStateException + * tryUnlockWrite fails if not write locked */ - public void testReadUnlock_IMSE2() { + public void testTryUnlockWrite_failure() { StampedLock lock = new StampedLock(); - try { - lock.unlockRead(0L); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + assertFalse(lock.tryUnlockWrite()); + + for (Function readLocker : readLockers()) + for (BiConsumer readUnlocker : readUnlockers()) { + long s = assertValid(lock, readLocker.apply(lock)); + assertFalse(lock.tryUnlockWrite()); + assertTrue(lock.isReadLocked()); + readUnlocker.accept(lock, s); + assertUnlocked(lock); + } } /** - * read-unlocking after writeLock throws IllegalMonitorStateException + * tryUnlockRead fails if not read locked */ - public void testReadUnlock_IMSE3() { + public void testTryUnlockRead_failure() { StampedLock lock = new StampedLock(); - long s = lock.writeLock(); - try { - lock.unlockRead(s); - shouldThrow(); - } catch (IllegalMonitorStateException success) {} + assertFalse(lock.tryUnlockRead()); + + for (Function writeLocker : writeLockers()) + for (BiConsumer writeUnlocker : writeUnlockers()) { + long s = writeLocker.apply(lock); + assertFalse(lock.tryUnlockRead()); + assertTrue(lock.isWriteLocked()); + writeUnlocker.accept(lock, s); + assertUnlocked(lock); + } } /** - * validate(0) fails + * validate(0L) fails */ public void testValidate0() { StampedLock lock = new StampedLock(); @@ -231,29 +220,24 @@ public class StampedLockTest extends JSR } /** - * 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 readLocker : readLockers()) + for (BiConsumer readUnlocker : readUnlockers()) { + long s = assertNonZero(readLocker.apply(lock)); + assertTrue(lock.validate(s)); + readUnlocker.accept(lock, s); + } + + for (Function writeLocker : writeLockers()) + for (BiConsumer writeUnlocker : writeUnlockers()) { + long s = assertNonZero(writeLocker.apply(lock)); + assertTrue(lock.validate(s)); + writeUnlocker.accept(lock, s); + } } /** @@ -261,124 +245,187 @@ public class StampedLockTest extends JSR */ 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); } + 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()); + } + } + /** - * writeLockInterruptibly is interruptible + * interruptible operations throw InterruptedException when pre-interrupted */ - public void testWriteLockInterruptibly_Interruptible() - throws InterruptedException { - final CountDownLatch running = new CountDownLatch(1); + public void testInterruptibleOperationsThrowInterruptedExceptionWhenPreInterrupted() { 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); + 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); } /** - * timed tryWriteLock is interruptible + * interruptible operations throw InterruptedException when write locked and interrupted */ - public void testWriteTryLock_Interruptible() throws InterruptedException { - final CountDownLatch running = new CountDownLatch(1); + public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() { 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 { - final CountDownLatch running = new CountDownLatch(1); + public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() { 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(); - running.await(); - waitForThreadToEnterWaitState(t, 100); - t.interrupt(); - awaitTermination(t); - releaseWriteLock(lock, s); + for (BiConsumer 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(); + + for (BiConsumer 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); } @@ -386,15 +433,15 @@ public class StampedLockTest extends JSR /** * 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); } @@ -408,13 +455,20 @@ public class StampedLockTest extends JSR 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); @@ -422,29 +476,32 @@ public class StampedLockTest extends JSR } /** - * A writelock succeeds only after a reading thread unlocks + * writeLock() succeeds only after a reading thread unlocks */ public void testWriteAfterReadLock() throws InterruptedException { - final CountDownLatch running = new CountDownLatch(1); + final CountDownLatch aboutToLock = new CountDownLatch(1); final StampedLock lock = new StampedLock(); long rs = lock.readLock(); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - running.countDown(); + aboutToLock.countDown(); long s = lock.writeLock(); + assertTrue(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); lock.unlockWrite(s); }}); - running.await(); - waitForThreadToEnterWaitState(t, 100); + await(aboutToLock); + waitForThreadToEnterWaitState(t); assertFalse(lock.isWriteLocked()); + assertTrue(lock.isReadLocked()); lock.unlockRead(rs); awaitTermination(t); - assertFalse(lock.isWriteLocked()); + assertUnlocked(lock); } /** - * A writelock succeeds only after reading threads unlock + * writeLock() succeeds only after reading threads unlock */ public void testWriteAfterMultipleReadLocks() { final StampedLock lock = new StampedLock(); @@ -463,36 +520,44 @@ public class StampedLockTest extends JSR lock.unlockWrite(ws); }}); + assertTrue(lock.isReadLocked()); assertFalse(lock.isWriteLocked()); lock.unlockRead(s); awaitTermination(t2); - assertFalse(lock.isWriteLocked()); + assertUnlocked(lock); } /** - * Readlocks succeed only after a writing thread unlocks + * readLock() succeed only after a writing thread unlocks */ 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() { - long rs = lock.readLock(); - lock.unlockRead(rs); - }}); - Thread t2 = newStartedThread(new CheckedRunnable() { + final Runnable acquireReleaseReadLock = new CheckedRunnable() { public void realRun() { + threadsStarted.countDown(); long rs = lock.readLock(); + assertTrue(lock.isReadLocked()); + assertFalse(lock.isWriteLocked()); lock.unlockRead(rs); - }}); - + }}; + Thread t1 = newStartedThread(acquireReleaseReadLock); + Thread t2 = newStartedThread(acquireReleaseReadLock); + + await(threadsStarted); + waitForThreadToEnterWaitState(t1); + waitForThreadToEnterWaitState(t2); + assertTrue(lock.isWriteLocked()); + assertFalse(lock.isReadLocked()); releaseWriteLock(lock, s); awaitTermination(t1); awaitTermination(t2); + assertUnlocked(lock); } /** - * tryReadLock succeeds if readlocked but not writelocked + * tryReadLock succeeds if read locked but not write locked */ public void testTryLockWhenReadLocked() { final StampedLock lock = new StampedLock(); @@ -500,7 +565,7 @@ public class StampedLockTest extends JSR Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { long rs = lock.tryReadLock(); - threadAssertTrue(rs != 0L); + assertValid(lock, rs); lock.unlockRead(rs); }}); @@ -509,15 +574,14 @@ public class StampedLockTest extends JSR } /** - * 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); @@ -525,86 +589,82 @@ public class StampedLockTest extends JSR } /** - * 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> 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(); - long timeoutMillis = 10; - long ws = lock.tryWriteLock(timeoutMillis, MILLISECONDS); - assertEquals(ws, 0L); - assertTrue(millisElapsedSince(startTime) >= timeoutMillis); - }}); + assertEquals(0L, lock.tryWriteLock(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }})); - awaitTermination(t); - releaseWriteLock(lock, s); - } + futures.add(cachedThreadPool.submit(new CheckedRunnable() { + public void realRun() throws InterruptedException { + long startTime = System.nanoTime(); + 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)); - /** - * 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() { + futures.add(cachedThreadPool.submit(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); - }}); + assertEquals(0L, lock2.tryWriteLock(timeoutMillis(), MILLISECONDS)); + assertTrue(millisElapsedSince(startTime) >= timeoutMillis()); + }})); - awaitTermination(t); - assertTrue(lock.isWriteLocked()); - lock.unlockWrite(s); + for (Future future : futures) + assertNull(future.get()); + + releaseWriteLock(lock, stamp); + releaseReadLock(lock2, stamp2); } /** - * writeLockInterruptibly succeeds if unlocked, else is interruptible + * writeLockInterruptibly succeeds if unlocked */ 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(); } /** @@ -636,54 +696,39 @@ public class StampedLockTest extends JSR } /** - * 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 writeLocker : writeLockers()) { + long s = assertValid(lock, writeLocker.apply(lock)); + assertEquals(0L, lock.tryOptimisticRead()); + releaseWriteLock(lock, s); + } + + for (Function 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); } @@ -694,186 +739,444 @@ public class StampedLockTest extends JSR */ public void testValidateOptimisticWriteLocked2() throws InterruptedException { - final CountDownLatch running = new CountDownLatch(1); + final CountDownLatch locked = 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(); - running.countDown(); + locked.countDown(); lock.writeLockInterruptibly(); }}); - running.await(); + await(locked); assertFalse(lock.validate(p)); - assertFalse((p = lock.tryOptimisticRead()) != 0L); + assertEquals(0L, lock.tryOptimisticRead()); + waitForThreadToEnterWaitState(t); t.interrupt(); awaitTermination(t); + assertTrue(lock.isWriteLocked()); } /** - * 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; - 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); + long s, p, q; + assertEquals(0L, lock.tryConvertToOptimisticRead(0L)); + + s = assertValid(lock, lock.tryOptimisticRead()); + assertEquals(s, lock.tryConvertToOptimisticRead(s)); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); - assertTrue(lock.validate(p)); + + for (Function writeLocker : writeLockers()) { + s = assertValid(lock, writeLocker.apply(lock)); + p = assertValid(lock, lock.tryConvertToOptimisticRead(s)); + assertFalse(lock.validate(s)); + assertTrue(lock.validate(p)); + assertUnlocked(lock); + } + + for (Function 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; - 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); + + assertEquals(0L, lock.tryConvertToReadLock(0L)); + + s = assertValid(lock, lock.tryOptimisticRead()); + p = assertValid(lock, lock.tryConvertToReadLock(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); 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)); + + s = assertValid(lock, lock.tryOptimisticRead()); + lock.readLock(); + p = assertValid(lock, lock.tryConvertToReadLock(s)); + assertTrue(lock.isReadLocked()); + assertEquals(2, lock.getReadLockCount()); 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); + assertUnlocked(lock); + + for (BiConsumer readUnlocker : readUnlockers()) { + for (Function 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); + } + + for (Function 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; - s = 0L; - assertFalse((p = lock.tryConvertToWriteLock(s)) != 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); - 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); + + for (BiConsumer writeUnlocker : writeUnlockers()) { + for (Function writeLocker : writeLockers()) { + s = assertValid(lock, writeLocker.apply(lock)); + assertEquals(s, lock.tryConvertToWriteLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isWriteLocked()); + writeUnlocker.accept(lock, s); + } + + for (Function 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); + } + } + + // failure if multiply read locked + for (Function 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); + } + } + + /** + * 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 = assertValid(sl, sl.tryOptimisticRead()); + 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(); + sl.readLock(); + long stamp = assertValid(sl, sl.tryOptimisticRead()); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = assertValid(sl, sl.tryOptimisticRead()); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + assertValid(sl, stamp); + 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()); + assertValid(sl, stamp); + 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()); + assertValid(sl, stamp); + 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); + } + + static long writeLockInterruptiblyUninterrupted(StampedLock sl) { + try { return sl.writeLockInterruptibly(); } + catch (InterruptedException ex) { throw new AssertionError(ex); } + } + + static long tryWriteLockUninterrupted(StampedLock sl, long time, TimeUnit unit) { + try { return sl.tryWriteLock(time, unit); } + catch (InterruptedException ex) { throw new AssertionError(ex); } + } + + static long readLockInterruptiblyUninterrupted(StampedLock sl) { + try { return sl.readLockInterruptibly(); } + catch (InterruptedException ex) { throw new AssertionError(ex); } + } + + static long tryReadLockUninterrupted(StampedLock sl, long time, TimeUnit unit) { + try { return sl.tryReadLock(time, unit); } + catch (InterruptedException ex) { throw new AssertionError(ex); } + } + + /** + * Invalid stamps result in IllegalMonitorStateException + */ + public void testInvalidStampsThrowIllegalMonitorStateException() { + final StampedLock sl = new StampedLock(); + + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockWrite(0L), + () -> sl.unlockRead(0L), + () -> sl.unlock(0L)); + + 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(); + + for (Function readLocker : readLockers()) + for (BiConsumer 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 writeLocker : writeLockers()) + for (BiConsumer writeUnlocker : writeUnlockers()) { + final long stamp = writeLocker.apply(sl); + assertValid(sl, stamp); + assertNoLongerValidStampsThrow.run(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp), + () -> sl.unlockWrite(0L)); + writeUnlocker.accept(sl, stamp); + assertUnlocked(sl); + assertNoLongerValidStampsThrow.run(); + } } + /** + * Read locks can be very deeply nested + */ + public void testDeeplyNestedReadLocks() { + final StampedLock lock = new StampedLock(); + final int depth = 300; + final long[] stamps = new long[depth]; + final List> readLockers = readLockers(); + final List> readUnlockers = readUnlockers(); + for (int i = 0; i < depth; i++) { + Function 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 readUnlocker + = readUnlockers.get(i % readUnlockers.size()); + assertEquals(depth - i, lock.getReadLockCount()); + assertTrue(lock.isReadLocked()); + readUnlocker.accept(lock, stamps[depth - 1 - i]); + } + assertUnlocked(lock); + } }