--- jsr166/src/test/tck/StampedLockTest.java 2017/02/18 14:32:09 1.33 +++ jsr166/src/test/tck/StampedLockTest.java 2019/02/22 19:27:47 1.43 @@ -7,7 +7,11 @@ import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.MILLISECONDS; -import static java.util.concurrent.TimeUnit.SECONDS; + +import static java.util.concurrent.locks.StampedLock.isLockStamp; +import static java.util.concurrent.locks.StampedLock.isOptimisticReadStamp; +import static java.util.concurrent.locks.StampedLock.isReadLockStamp; +import static java.util.concurrent.locks.StampedLock.isWriteLockStamp; import java.util.ArrayList; import java.util.List; @@ -248,9 +252,11 @@ public class StampedLockTest extends JSR long s = assertNonZero(lock.writeLock()); assertTrue(lock.validate(s)); assertFalse(lock.validate(lock.tryWriteLock())); - assertFalse(lock.validate(lock.tryWriteLock(0L, SECONDS))); + assertFalse(lock.validate(lock.tryWriteLock(randomExpiredTimeout(), + randomTimeUnit()))); assertFalse(lock.validate(lock.tryReadLock())); - assertFalse(lock.validate(lock.tryReadLock(0L, SECONDS))); + assertFalse(lock.validate(lock.tryWriteLock(randomExpiredTimeout(), + randomTimeUnit()))); assertFalse(lock.validate(lock.tryOptimisticRead())); lock.unlockWrite(s); } @@ -491,8 +497,8 @@ public class StampedLockTest extends JSR lock.unlockWrite(s); }}); - aboutToLock.await(); - waitForThreadToEnterWaitState(t); + await(aboutToLock); + assertThreadBlocks(t, Thread.State.WAITING); assertFalse(lock.isWriteLocked()); assertTrue(lock.isReadLocked()); lock.unlockRead(rs); @@ -546,8 +552,8 @@ public class StampedLockTest extends JSR Thread t2 = newStartedThread(acquireReleaseReadLock); await(threadsStarted); - waitForThreadToEnterWaitState(t1); - waitForThreadToEnterWaitState(t2); + assertThreadBlocks(t1, Thread.State.WAITING); + assertThreadBlocks(t2, Thread.State.WAITING); assertTrue(lock.isWriteLocked()); assertFalse(lock.isReadLocked()); releaseWriteLock(lock, s); @@ -581,7 +587,7 @@ public class StampedLockTest extends JSR long s = lock.readLock(); Thread t = newStartedThread(new CheckedRunnable() { public void realRun() { - threadAssertEquals(0L, lock.tryWriteLock()); + assertEquals(0L, lock.tryWriteLock()); }}); awaitTermination(t); @@ -750,10 +756,10 @@ public class StampedLockTest extends JSR lock.writeLockInterruptibly(); }}); - locked.await(); + await(locked); assertFalse(lock.validate(p)); assertEquals(0L, lock.tryOptimisticRead()); - waitForThreadToEnterWaitState(t); + assertThreadBlocks(t, Thread.State.WAITING); t.interrupt(); awaitTermination(t); assertTrue(lock.isWriteLocked()); @@ -976,106 +982,117 @@ public class StampedLockTest extends JSR * 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 = assertValid(sl, sl.tryOptimisticRead()); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + assertThrows(IllegalMonitorStateException.class, + () -> 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); - }, - }; + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.writeLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } + { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = assertValid(sl, sl.tryOptimisticRead()); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp)); + } + { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = assertValid(sl, sl.tryOptimisticRead()); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } - assertThrows(IllegalMonitorStateException.class, actions); + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + assertValid(sl, stamp); + sl.writeLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockWrite(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.writeLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } + + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertValid(sl, stamp); + sl.writeLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockWrite(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.writeLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp)); + } + { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertValid(sl, stamp); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlockRead(stamp)); + } + { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } + { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + assertThrows(IllegalMonitorStateException.class, + () -> sl.unlock(stamp)); + } } static long writeLockInterruptiblyUninterrupted(StampedLock sl) { @@ -1179,4 +1196,194 @@ public class StampedLockTest extends JSR } assertUnlocked(lock); } + + /** + * Stamped locks are not reentrant. + */ + public void testNonReentrant() throws InterruptedException { + final StampedLock lock = new StampedLock(); + long stamp; + + stamp = lock.writeLock(); + assertValid(lock, stamp); + assertEquals(0L, lock.tryWriteLock(0L, DAYS)); + assertEquals(0L, lock.tryReadLock(0L, DAYS)); + assertValid(lock, stamp); + lock.unlockWrite(stamp); + + stamp = lock.tryWriteLock(1L, DAYS); + assertEquals(0L, lock.tryWriteLock(0L, DAYS)); + assertValid(lock, stamp); + lock.unlockWrite(stamp); + + stamp = lock.readLock(); + assertEquals(0L, lock.tryWriteLock(0L, DAYS)); + assertValid(lock, stamp); + lock.unlockRead(stamp); + } + + /** + * """StampedLocks have no notion of ownership. Locks acquired in + * one thread can be released or converted in another.""" + */ + public void testNoOwnership() throws Throwable { + ArrayList> futures = new ArrayList<>(); + for (Function writeLocker : writeLockers()) + for (BiConsumer writeUnlocker : writeUnlockers()) { + StampedLock lock = new StampedLock(); + long stamp = writeLocker.apply(lock); + futures.add(cachedThreadPool.submit(new CheckedRunnable() { + public void realRun() { + writeUnlocker.accept(lock, stamp); + assertUnlocked(lock); + assertFalse(lock.validate(stamp)); + }})); + } + for (Future future : futures) + assertNull(future.get()); + } + + /** Tries out sample usage code from StampedLock javadoc. */ + public void testSampleUsage() throws Throwable { + class Point { + private double x, y; + private final StampedLock sl = new StampedLock(); + + void move(double deltaX, double deltaY) { // an exclusively locked method + long stamp = sl.writeLock(); + try { + x += deltaX; + y += deltaY; + } finally { + sl.unlockWrite(stamp); + } + } + + double distanceFromOrigin() { // A read-only method + double currentX, currentY; + long stamp = sl.tryOptimisticRead(); + do { + if (stamp == 0L) + stamp = sl.readLock(); + try { + // possibly racy reads + currentX = x; + currentY = y; + } finally { + stamp = sl.tryConvertToOptimisticRead(stamp); + } + } while (stamp == 0); + return Math.hypot(currentX, currentY); + } + + double distanceFromOrigin2() { + long stamp = sl.tryOptimisticRead(); + try { + retryHoldingLock: + for (;; stamp = sl.readLock()) { + if (stamp == 0L) + continue retryHoldingLock; + // possibly racy reads + double currentX = x; + double currentY = y; + if (!sl.validate(stamp)) + continue retryHoldingLock; + return Math.hypot(currentX, currentY); + } + } finally { + if (StampedLock.isReadLockStamp(stamp)) + sl.unlockRead(stamp); + } + } + + void moveIfAtOrigin(double newX, double newY) { + long stamp = sl.readLock(); + try { + while (x == 0.0 && y == 0.0) { + long ws = sl.tryConvertToWriteLock(stamp); + if (ws != 0L) { + stamp = ws; + x = newX; + y = newY; + return; + } + else { + sl.unlockRead(stamp); + stamp = sl.writeLock(); + } + } + } finally { + sl.unlock(stamp); + } + } + } + + Point p = new Point(); + p.move(3.0, 4.0); + assertEquals(5.0, p.distanceFromOrigin()); + p.moveIfAtOrigin(5.0, 12.0); + assertEquals(5.0, p.distanceFromOrigin2()); + } + + /** + * Stamp inspection methods work as expected, and do not inspect + * the state of the lock itself. + */ + public void testStampStateInspectionMethods() { + StampedLock lock = new StampedLock(); + + assertFalse(isWriteLockStamp(0L)); + assertFalse(isReadLockStamp(0L)); + assertFalse(isLockStamp(0L)); + assertFalse(isOptimisticReadStamp(0L)); + + { + long stamp = lock.writeLock(); + for (int i = 0; i < 2; i++) { + assertTrue(isWriteLockStamp(stamp)); + assertFalse(isReadLockStamp(stamp)); + assertTrue(isLockStamp(stamp)); + assertFalse(isOptimisticReadStamp(stamp)); + if (i == 0) + lock.unlockWrite(stamp); + } + } + + { + long stamp = lock.readLock(); + for (int i = 0; i < 2; i++) { + assertFalse(isWriteLockStamp(stamp)); + assertTrue(isReadLockStamp(stamp)); + assertTrue(isLockStamp(stamp)); + assertFalse(isOptimisticReadStamp(stamp)); + if (i == 0) + lock.unlockRead(stamp); + } + } + + { + long optimisticStamp = lock.tryOptimisticRead(); + long readStamp = lock.tryConvertToReadLock(optimisticStamp); + long writeStamp = lock.tryConvertToWriteLock(readStamp); + for (int i = 0; i < 2; i++) { + assertFalse(isWriteLockStamp(optimisticStamp)); + assertFalse(isReadLockStamp(optimisticStamp)); + assertFalse(isLockStamp(optimisticStamp)); + assertTrue(isOptimisticReadStamp(optimisticStamp)); + + assertFalse(isWriteLockStamp(readStamp)); + assertTrue(isReadLockStamp(readStamp)); + assertTrue(isLockStamp(readStamp)); + assertFalse(isOptimisticReadStamp(readStamp)); + + assertTrue(isWriteLockStamp(writeStamp)); + assertFalse(isReadLockStamp(writeStamp)); + assertTrue(isLockStamp(writeStamp)); + assertFalse(isOptimisticReadStamp(writeStamp)); + if (i == 0) + lock.unlockWrite(writeStamp); + } + } + } + }