blob: 65ef48d046b2a2e5c4da21977a549b6f9e9d8f79 [file] [log] [blame]
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* This file is available under and governed by the GNU General Public
* License version 2 only, as published by the Free Software Foundation.
* However, the following notice accompanied the original version of this
* file:
*
* 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/
*/
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;
public class StampedLockTest extends JSR166TestCase {
public static void main(String[] args) {
main(suite(), args);
}
public static Test suite() {
return new TestSuite(StampedLockTest.class);
}
/**
* Releases write lock, checking isWriteLocked before and after
*/
void releaseWriteLock(StampedLock lock, long stamp) {
assertTrue(lock.isWriteLocked());
assertValid(lock, stamp);
lock.unlockWrite(stamp);
assertFalse(lock.isWriteLocked());
assertFalse(lock.validate(stamp));
}
/**
* Releases read lock, checking isReadLocked before and after
*/
void releaseReadLock(StampedLock lock, long stamp) {
assertTrue(lock.isReadLocked());
assertValid(lock, stamp);
lock.unlockRead(stamp);
assertFalse(lock.isReadLocked());
assertTrue(lock.validate(stamp));
}
long assertNonZero(long v) {
assertTrue(v != 0L);
return v;
}
long assertValid(StampedLock lock, long stamp) {
assertTrue(stamp != 0L);
assertTrue(lock.validate(stamp));
return stamp;
}
void assertUnlocked(StampedLock lock) {
assertFalse(lock.isReadLocked());
assertFalse(lock.isWriteLocked());
assertEquals(0, lock.getReadLockCount());
assertValid(lock, lock.tryOptimisticRead());
}
List<Action> lockLockers(Lock lock) {
List<Action> 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<Function<StampedLock, Long>> readLockers() {
List<Function<StampedLock, Long>> 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<BiConsumer<StampedLock, Long>> readUnlockers() {
List<BiConsumer<StampedLock, Long>> 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<Function<StampedLock, Long>> writeLockers() {
List<Function<StampedLock, Long>> 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<BiConsumer<StampedLock, Long>> writeUnlockers() {
List<BiConsumer<StampedLock, Long>> 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;
}
/**
* Constructed StampedLock is in unlocked state
*/
public void testConstructor() {
assertUnlocked(new StampedLock());
}
/**
* write-locking, then unlocking, an unlocked lock succeed
*/
public void testWriteLock_lockUnlock() {
StampedLock lock = new StampedLock();
for (Function<StampedLock, Long> writeLocker : writeLockers())
for (BiConsumer<StampedLock, Long> 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-locking, then unlocking, an unlocked lock succeed
*/
public void testReadLock_lockUnlock() {
StampedLock lock = new StampedLock();
for (Function<StampedLock, Long> readLocker : readLockers())
for (BiConsumer<StampedLock, Long> 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);
}
}
/**
* tryUnlockWrite fails if not write locked
*/
public void testTryUnlockWrite_failure() {
StampedLock lock = new StampedLock();
assertFalse(lock.tryUnlockWrite());
for (Function<StampedLock, Long> readLocker : readLockers())
for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
long s = assertValid(lock, readLocker.apply(lock));
assertFalse(lock.tryUnlockWrite());
assertTrue(lock.isReadLocked());
readUnlocker.accept(lock, s);
assertUnlocked(lock);
}
}
/**
* tryUnlockRead fails if not read locked
*/
public void testTryUnlockRead_failure() {
StampedLock lock = new StampedLock();
assertFalse(lock.tryUnlockRead());
for (Function<StampedLock, Long> writeLocker : writeLockers())
for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
long s = writeLocker.apply(lock);
assertFalse(lock.tryUnlockRead());
assertTrue(lock.isWriteLocked());
writeUnlocker.accept(lock, s);
assertUnlocked(lock);
}
}
/**
* validate(0L) fails
*/
public void testValidate0() {
StampedLock lock = new StampedLock();
assertFalse(lock.validate(0L));
}
/**
* A stamp obtained from a successful lock operation validates while the lock is held
*/
public void testValidate() throws InterruptedException {
StampedLock lock = new StampedLock();
for (Function<StampedLock, Long> readLocker : readLockers())
for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
long s = assertNonZero(readLocker.apply(lock));
assertTrue(lock.validate(s));
readUnlocker.accept(lock, s);
}
for (Function<StampedLock, Long> writeLocker : writeLockers())
for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
long s = assertNonZero(writeLocker.apply(lock));
assertTrue(lock.validate(s));
writeUnlocker.accept(lock, s);
}
}
/**
* A stamp obtained from an unsuccessful lock operation does not validate
*/
public void testValidate2() throws InterruptedException {
StampedLock lock = new StampedLock();
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.tryReadLock()));
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());
}
}
/**
* interruptible operations throw InterruptedException when pre-interrupted
*/
public void testInterruptibleOperationsThrowInterruptedExceptionWhenPreInterrupted() {
final StampedLock lock = new StampedLock();
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);
}
/**
* interruptible operations throw InterruptedException when write locked and interrupted
*/
public void testInterruptibleOperationsThrowInterruptedExceptionWriteLockedInterrupted() {
final StampedLock lock = new StampedLock();
long s = lock.writeLock();
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);
}
/**
* interruptible operations throw InterruptedException when read locked and interrupted
*/
public void testInterruptibleOperationsThrowInterruptedExceptionReadLockedInterrupted() {
final StampedLock lock = new StampedLock();
long s = lock.readLock();
Action[] interruptibleLockBlockingActions = {
() -> lock.writeLockInterruptibly(),
() -> lock.tryWriteLock(Long.MAX_VALUE, DAYS),
() -> lock.asWriteLock().lockInterruptibly(),
() -> lock.asWriteLock().tryLock(Long.MAX_VALUE, DAYS),
};
shuffle(interruptibleLockBlockingActions);
assertThrowInterruptedExceptionWhenInterrupted(interruptibleLockBlockingActions);
}
/**
* Non-interruptible operations ignore and preserve interrupt status
*/
public void testNonInterruptibleOperationsIgnoreInterrupts() {
final StampedLock lock = new StampedLock();
Thread.currentThread().interrupt();
for (BiConsumer<StampedLock, Long> 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<StampedLock, Long> 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 testTryWriteLock() {
final StampedLock lock = new StampedLock();
long s = lock.tryWriteLock();
assertTrue(s != 0L);
assertTrue(lock.isWriteLocked());
assertEquals(0L, lock.tryWriteLock());
releaseWriteLock(lock, s);
}
/**
* tryWriteLock fails if locked
*/
public void testTryWriteLockWhenLocked() {
final StampedLock lock = new StampedLock();
long s = lock.writeLock();
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
assertEquals(0L, lock.tryWriteLock());
}});
assertEquals(0L, lock.tryWriteLock());
awaitTermination(t);
releaseWriteLock(lock, s);
}
/**
* tryReadLock fails if write-locked
*/
public void testTryReadLockWhenLocked() {
final StampedLock lock = new StampedLock();
long s = lock.writeLock();
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
assertEquals(0L, lock.tryReadLock());
}});
assertEquals(0L, lock.tryReadLock());
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();
assertValid(lock, s2);
lock.unlockRead(s2);
long s3 = lock.tryReadLock(LONG_DELAY_MS, MILLISECONDS);
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);
lock.unlockRead(s);
}
/**
* writeLock() succeeds only after a reading thread unlocks
*/
public void testWriteAfterReadLock() throws InterruptedException {
final CountDownLatch aboutToLock = new CountDownLatch(1);
final StampedLock lock = new StampedLock();
long rs = lock.readLock();
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
aboutToLock.countDown();
long s = lock.writeLock();
assertTrue(lock.isWriteLocked());
assertFalse(lock.isReadLocked());
lock.unlockWrite(s);
}});
aboutToLock.await();
waitForThreadToEnterWaitState(t);
assertFalse(lock.isWriteLocked());
assertTrue(lock.isReadLocked());
lock.unlockRead(rs);
awaitTermination(t);
assertUnlocked(lock);
}
/**
* 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);
}});
assertTrue(lock.isReadLocked());
assertFalse(lock.isWriteLocked());
lock.unlockRead(s);
awaitTermination(t2);
assertUnlocked(lock);
}
/**
* 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();
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 read locked but not write locked
*/
public void testTryLockWhenReadLocked() {
final StampedLock lock = new StampedLock();
long s = lock.readLock();
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
long rs = lock.tryReadLock();
assertValid(lock, rs);
lock.unlockRead(rs);
}});
awaitTermination(t);
lock.unlockRead(s);
}
/**
* tryWriteLock fails when read locked
*/
public void testTryWriteLockWhenReadLocked() {
final StampedLock lock = new StampedLock();
long s = lock.readLock();
Thread t = newStartedThread(new CheckedRunnable() {
public void realRun() {
threadAssertEquals(0L, lock.tryWriteLock());
}});
awaitTermination(t);
lock.unlockRead(s);
}
/**
* timed lock operations time out if lock not available
*/
public void testTimedLock_Timeout() throws Exception {
ArrayList<Future<?>> futures = new ArrayList<>();
// Write locked
final StampedLock lock = new StampedLock();
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();
assertEquals(0L, lock.tryWriteLock(timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}}));
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));
futures.add(cachedThreadPool.submit(new CheckedRunnable() {
public void realRun() throws InterruptedException {
long startTime = System.nanoTime();
assertEquals(0L, lock2.tryWriteLock(timeoutMillis(), MILLISECONDS));
assertTrue(millisElapsedSince(startTime) >= timeoutMillis());
}}));
for (Future<?> future : futures)
assertNull(future.get());
releaseWriteLock(lock, stamp);
releaseReadLock(lock2, stamp2);
}
/**
* writeLockInterruptibly succeeds if unlocked
*/
public void testWriteLockInterruptibly() throws InterruptedException {
final StampedLock lock = new StampedLock();
long s = lock.writeLockInterruptibly();
assertTrue(lock.isWriteLocked());
releaseWriteLock(lock, s);
}
/**
* readLockInterruptibly succeeds if lock free
*/
public void testReadLockInterruptibly() throws InterruptedException {
final StampedLock lock = new StampedLock();
long s = assertValid(lock, lock.readLockInterruptibly());
assertTrue(lock.isReadLocked());
lock.unlockRead(s);
lock.asReadLock().lockInterruptibly();
assertTrue(lock.isReadLocked());
lock.asReadLock().unlock();
}
/**
* 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
* exclusively locked
*/
public void testValidateOptimistic() throws InterruptedException {
StampedLock lock = new StampedLock();
assertValid(lock, lock.tryOptimisticRead());
for (Function<StampedLock, Long> writeLocker : writeLockers()) {
long s = assertValid(lock, writeLocker.apply(lock));
assertEquals(0L, lock.tryOptimisticRead());
releaseWriteLock(lock, s);
}
for (Function<StampedLock, Long> 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() {
final StampedLock lock = new StampedLock();
final long p = assertValid(lock, lock.tryOptimisticRead());
final long s = assertValid(lock, lock.writeLock());
assertFalse(lock.validate(p));
assertEquals(0L, lock.tryOptimisticRead());
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 locked = new CountDownLatch(1);
final StampedLock lock = new StampedLock();
final long p = assertValid(lock, lock.tryOptimisticRead());
Thread t = newStartedThread(new CheckedInterruptedRunnable() {
public void realRun() throws InterruptedException {
lock.writeLockInterruptibly();
locked.countDown();
lock.writeLockInterruptibly();
}});
locked.await();
assertFalse(lock.validate(p));
assertEquals(0L, lock.tryOptimisticRead());
waitForThreadToEnterWaitState(t);
t.interrupt();
awaitTermination(t);
assertTrue(lock.isWriteLocked());
}
/**
* tryConvertToOptimisticRead succeeds and validates if successfully locked
*/
public void testTryConvertToOptimisticRead() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p, q;
assertEquals(0L, lock.tryConvertToOptimisticRead(0L));
s = assertValid(lock, lock.tryOptimisticRead());
assertEquals(s, lock.tryConvertToOptimisticRead(s));
assertTrue(lock.validate(s));
for (Function<StampedLock, Long> 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<StampedLock, Long> 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 for valid stamps
*/
public void testTryConvertToReadLock() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p;
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));
lock.unlockRead(p);
s = assertValid(lock, lock.tryOptimisticRead());
lock.readLock();
p = assertValid(lock, lock.tryConvertToReadLock(s));
assertTrue(lock.isReadLocked());
assertEquals(2, lock.getReadLockCount());
lock.unlockRead(p);
lock.unlockRead(p);
assertUnlocked(lock);
for (BiConsumer<StampedLock, Long> readUnlocker : readUnlockers()) {
for (Function<StampedLock, Long> 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<StampedLock, Long> 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 if lock available; fails if multiply read locked
*/
public void testTryConvertToWriteLock() throws InterruptedException {
StampedLock lock = new StampedLock();
long s, p;
assertEquals(0L, lock.tryConvertToWriteLock(0L));
assertTrue((s = lock.tryOptimisticRead()) != 0L);
assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L);
assertTrue(lock.isWriteLocked());
lock.unlockWrite(p);
for (BiConsumer<StampedLock, Long> writeUnlocker : writeUnlockers()) {
for (Function<StampedLock, Long> 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<StampedLock, Long> 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<StampedLock, Long> 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() throws Throwable {
StampedLock sl = new StampedLock();
Lock lock = sl.asWriteLock();
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() throws Throwable {
StampedLock sl = new StampedLock();
Lock lock = sl.asReadLock();
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() throws Throwable {
StampedLock sl = new StampedLock();
Lock lock = sl.asReadWriteLock().writeLock();
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() throws Throwable {
StampedLock sl = new StampedLock();
Lock lock = sl.asReadWriteLock().readLock();
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<StampedLock, Long> readLocker : readLockers())
for (BiConsumer<StampedLock, Long> 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<StampedLock, Long> writeLocker : writeLockers())
for (BiConsumer<StampedLock, Long> 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<Function<StampedLock, Long>> readLockers = readLockers();
final List<BiConsumer<StampedLock, Long>> readUnlockers = readUnlockers();
for (int i = 0; i < depth; i++) {
Function<StampedLock, Long> 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<StampedLock, Long> readUnlocker
= readUnlockers.get(i % readUnlockers.size());
assertEquals(depth - i, lock.getReadLockCount());
assertTrue(lock.isReadLocked());
readUnlocker.accept(lock, stamps[depth - 1 - i]);
}
assertUnlocked(lock);
}
}