--- jsr166/src/test/tck/StampedLockTest.java 2017/11/27 23:38:11 1.42 +++ jsr166/src/test/tck/StampedLockTest.java 2020/08/12 16:15:28 1.46 @@ -15,12 +15,17 @@ import static java.util.concurrent.locks import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Future; +import java.util.concurrent.ThreadLocalRandom; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.StampedLock; import java.util.function.BiConsumer; +import java.util.function.Consumer; import java.util.function.Function; import junit.framework.Test; @@ -75,56 +80,51 @@ public class StampedLockTest extends JSR } 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; + return List.of( + () -> lock.lock(), + () -> lock.lockInterruptibly(), + () -> lock.tryLock(), + () -> lock.tryLock(Long.MIN_VALUE, DAYS), + () -> lock.tryLock(0L, DAYS), + () -> lock.tryLock(Long.MAX_VALUE, DAYS)); } 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; + return List.of( + sl -> sl.readLock(), + sl -> sl.tryReadLock(), + sl -> readLockInterruptiblyUninterrupted(sl), + sl -> tryReadLockUninterrupted(sl, Long.MIN_VALUE, DAYS), + sl -> tryReadLockUninterrupted(sl, 0L, DAYS), + sl -> sl.tryConvertToReadLock(sl.tryOptimisticRead())); } 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; + return List.of( + (sl, stamp) -> sl.unlockRead(stamp), + (sl, stamp) -> assertTrue(sl.tryUnlockRead()), + (sl, stamp) -> sl.asReadLock().unlock(), + (sl, stamp) -> sl.unlock(stamp), + (sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); } 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; + return List.of( + sl -> sl.writeLock(), + sl -> sl.tryWriteLock(), + sl -> writeLockInterruptiblyUninterrupted(sl), + sl -> tryWriteLockUninterrupted(sl, Long.MIN_VALUE, DAYS), + sl -> tryWriteLockUninterrupted(sl, 0L, DAYS), + sl -> sl.tryConvertToWriteLock(sl.tryOptimisticRead())); } 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; + return List.of( + (sl, stamp) -> sl.unlockWrite(stamp), + (sl, stamp) -> assertTrue(sl.tryUnlockWrite()), + (sl, stamp) -> sl.asWriteLock().unlock(), + (sl, stamp) -> sl.unlock(stamp), + (sl, stamp) -> assertValid(sl, sl.tryConvertToOptimisticRead(stamp))); } /** @@ -343,7 +343,7 @@ public class StampedLockTest extends JSR */ public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() { final StampedLock lock = new StampedLock(); - long s = lock.writeLock(); + long stamp = lock.writeLock(); Action[] interruptibleLockBlockingActions = { () -> lock.writeLockInterruptibly(), @@ -358,6 +358,8 @@ public class StampedLockTest extends JSR shuffle(interruptibleLockBlockingActions); assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions); + + releaseWriteLock(lock, stamp); } /** @@ -365,7 +367,7 @@ public class StampedLockTest extends JSR */ public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() { final StampedLock lock = new StampedLock(); - long s = lock.readLock(); + long stamp = lock.readLock(); Action[] interruptibleLockBlockingActions = { () -> lock.writeLockInterruptibly(), @@ -376,6 +378,8 @@ public class StampedLockTest extends JSR shuffle(interruptibleLockBlockingActions); assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions); + + releaseReadLock(lock, stamp); } /** @@ -699,6 +703,7 @@ public class StampedLockTest extends JSR lock.unlockWrite(s); s = lock.readLock(); assertTrue(lock.toString().contains("Read-locks")); + releaseReadLock(lock, s); } /** @@ -982,106 +987,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 = 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 = 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.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.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)); + } - () -> { - 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.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)); + } - assertThrows(IllegalMonitorStateException.class, actions); + { + 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) { @@ -1375,4 +1391,113 @@ public class StampedLockTest extends JSR } } + /** + * Multiple threads repeatedly contend for the same lock. + */ + public void testConcurrentAccess() throws Exception { + final StampedLock sl = new StampedLock(); + final Lock wl = sl.asWriteLock(); + final Lock rl = sl.asReadLock(); + final long testDurationMillis = expensiveTests ? 1000 : 2; + final int nTasks = ThreadLocalRandom.current().nextInt(1, 10); + final AtomicBoolean done = new AtomicBoolean(false); + final List futures = new ArrayList<>(); + final List> stampedWriteLockers = List.of( + () -> sl.writeLock(), + () -> writeLockInterruptiblyUninterrupted(sl), + () -> tryWriteLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS), + () -> { + long stamp; + do { stamp = sl.tryConvertToWriteLock(sl.tryOptimisticRead()); } + while (stamp == 0L); + return stamp; + }, + () -> { + long stamp; + do { stamp = sl.tryWriteLock(); } while (stamp == 0L); + return stamp; + }, + () -> { + long stamp; + do { stamp = sl.tryWriteLock(0L, DAYS); } while (stamp == 0L); + return stamp; + }); + final List> stampedReadLockers = List.of( + () -> sl.readLock(), + () -> readLockInterruptiblyUninterrupted(sl), + () -> tryReadLockUninterrupted(sl, LONG_DELAY_MS, MILLISECONDS), + () -> { + long stamp; + do { stamp = sl.tryConvertToReadLock(sl.tryOptimisticRead()); } + while (stamp == 0L); + return stamp; + }, + () -> { + long stamp; + do { stamp = sl.tryReadLock(); } while (stamp == 0L); + return stamp; + }, + () -> { + long stamp; + do { stamp = sl.tryReadLock(0L, DAYS); } while (stamp == 0L); + return stamp; + }); + final List> stampedWriteUnlockers = List.of( + stamp -> sl.unlockWrite(stamp), + stamp -> sl.unlock(stamp), + stamp -> assertTrue(sl.tryUnlockWrite()), + stamp -> wl.unlock(), + stamp -> sl.tryConvertToOptimisticRead(stamp)); + final List> stampedReadUnlockers = List.of( + stamp -> sl.unlockRead(stamp), + stamp -> sl.unlock(stamp), + stamp -> assertTrue(sl.tryUnlockRead()), + stamp -> rl.unlock(), + stamp -> sl.tryConvertToOptimisticRead(stamp)); + final Action writer = () -> { + // repeatedly acquires write lock + var locker = chooseRandomly(stampedWriteLockers); + var unlocker = chooseRandomly(stampedWriteUnlockers); + while (!done.getAcquire()) { + long stamp = locker.call(); + try { + assertTrue(isWriteLockStamp(stamp)); + assertTrue(sl.isWriteLocked()); + assertFalse(isReadLockStamp(stamp)); + assertFalse(sl.isReadLocked()); + assertEquals(0, sl.getReadLockCount()); + assertTrue(sl.validate(stamp)); + } finally { + unlocker.accept(stamp); + } + } + }; + final Action reader = () -> { + // repeatedly acquires read lock + var locker = chooseRandomly(stampedReadLockers); + var unlocker = chooseRandomly(stampedReadUnlockers); + while (!done.getAcquire()) { + long stamp = locker.call(); + try { + assertFalse(isWriteLockStamp(stamp)); + assertFalse(sl.isWriteLocked()); + assertTrue(isReadLockStamp(stamp)); + assertTrue(sl.isReadLocked()); + assertTrue(sl.getReadLockCount() > 0); + assertTrue(sl.validate(stamp)); + } finally { + unlocker.accept(stamp); + } + } + }; + for (int i = nTasks; i--> 0; ) { + Action task = chooseRandomly(writer, reader); + futures.add(CompletableFuture.runAsync(checkedRunnable(task))); + } + Thread.sleep(testDurationMillis); + done.setRelease(true); + for (var future : futures) + checkTimedGet(future, null); + } + }