--- jsr166/src/test/tck/StampedLockTest.java 2019/02/22 19:27:47 1.43 +++ jsr166/src/test/tck/StampedLockTest.java 2019/09/29 20:19:43 1.44 @@ -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))); } /** @@ -1386,4 +1386,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); + } + }