6625723: Excessive ThreadLocal storage used by ReentrantReadWriteLock
Reviewed-by: dholmes
This commit is contained in:
parent
2bcc7a86a0
commit
e4f30f8084
@ -222,7 +222,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
/** Inner class providing writelock */
|
/** Inner class providing writelock */
|
||||||
private final ReentrantReadWriteLock.WriteLock writerLock;
|
private final ReentrantReadWriteLock.WriteLock writerLock;
|
||||||
/** Performs all synchronization mechanics */
|
/** Performs all synchronization mechanics */
|
||||||
private final Sync sync;
|
final Sync sync;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new {@code ReentrantReadWriteLock} with
|
* Creates a new {@code ReentrantReadWriteLock} with
|
||||||
@ -239,7 +239,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
* @param fair {@code true} if this lock should use a fair ordering policy
|
* @param fair {@code true} if this lock should use a fair ordering policy
|
||||||
*/
|
*/
|
||||||
public ReentrantReadWriteLock(boolean fair) {
|
public ReentrantReadWriteLock(boolean fair) {
|
||||||
sync = (fair)? new FairSync() : new NonfairSync();
|
sync = fair ? new FairSync() : new NonfairSync();
|
||||||
readerLock = new ReadLock(this);
|
readerLock = new ReadLock(this);
|
||||||
writerLock = new WriteLock(this);
|
writerLock = new WriteLock(this);
|
||||||
}
|
}
|
||||||
@ -256,8 +256,8 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Read vs write count extraction constants and functions.
|
* Read vs write count extraction constants and functions.
|
||||||
* Lock state is logically divided into two shorts: The lower
|
* Lock state is logically divided into two unsigned shorts:
|
||||||
* one representing the exclusive (writer) lock hold count,
|
* The lower one representing the exclusive (writer) lock hold count,
|
||||||
* and the upper the shared (reader) hold count.
|
* and the upper the shared (reader) hold count.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -279,13 +279,6 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
int count;
|
int count;
|
||||||
// Use id, not reference, to avoid garbage retention
|
// Use id, not reference, to avoid garbage retention
|
||||||
final long tid = Thread.currentThread().getId();
|
final long tid = Thread.currentThread().getId();
|
||||||
/** Decrement if positive; return previous value */
|
|
||||||
int tryDecrement() {
|
|
||||||
int c = count;
|
|
||||||
if (c > 0)
|
|
||||||
count = c - 1;
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -303,7 +296,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
* The number of read locks held by current thread.
|
* The number of read locks held by current thread.
|
||||||
* Initialized only in constructor and readObject.
|
* Initialized only in constructor and readObject.
|
||||||
*/
|
*/
|
||||||
transient ThreadLocalHoldCounter readHolds;
|
private transient ThreadLocalHoldCounter readHolds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The hold count of the last thread to successfully acquire
|
* The hold count of the last thread to successfully acquire
|
||||||
@ -312,7 +305,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
* acquire. This is non-volatile since it is just used
|
* acquire. This is non-volatile since it is just used
|
||||||
* as a heuristic, and would be great for threads to cache.
|
* as a heuristic, and would be great for threads to cache.
|
||||||
*/
|
*/
|
||||||
transient HoldCounter cachedHoldCounter;
|
private transient HoldCounter cachedHoldCounter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* firstReader is the first thread to have acquired the read lock.
|
||||||
|
* firstReaderHoldCount is firstReader's hold count.
|
||||||
|
* This allows tracking of read holds for uncontended read
|
||||||
|
* locks to be very cheap.
|
||||||
|
*/
|
||||||
|
private final static long INVALID_THREAD_ID = -1;
|
||||||
|
private transient long firstReader = INVALID_THREAD_ID;
|
||||||
|
private transient int firstReaderHoldCount;
|
||||||
|
|
||||||
Sync() {
|
Sync() {
|
||||||
readHolds = new ThreadLocalHoldCounter();
|
readHolds = new ThreadLocalHoldCounter();
|
||||||
@ -390,12 +393,25 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean tryReleaseShared(int unused) {
|
protected final boolean tryReleaseShared(int unused) {
|
||||||
HoldCounter rh = cachedHoldCounter;
|
long tid = Thread.currentThread().getId();
|
||||||
Thread current = Thread.currentThread();
|
if (firstReader == tid) {
|
||||||
if (rh == null || rh.tid != current.getId())
|
// assert firstReaderHoldCount > 0;
|
||||||
rh = readHolds.get();
|
if (firstReaderHoldCount == 1)
|
||||||
if (rh.tryDecrement() <= 0)
|
firstReader = INVALID_THREAD_ID;
|
||||||
throw new IllegalMonitorStateException();
|
else
|
||||||
|
firstReaderHoldCount--;
|
||||||
|
} else {
|
||||||
|
HoldCounter rh = cachedHoldCounter;
|
||||||
|
if (rh == null || rh.tid != tid)
|
||||||
|
rh = readHolds.get();
|
||||||
|
int count = rh.count;
|
||||||
|
if (count <= 1) {
|
||||||
|
readHolds.remove();
|
||||||
|
if (count <= 0)
|
||||||
|
throw unmatchedUnlockException();
|
||||||
|
}
|
||||||
|
--rh.count;
|
||||||
|
}
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = getState();
|
int c = getState();
|
||||||
int nextc = c - SHARED_UNIT;
|
int nextc = c - SHARED_UNIT;
|
||||||
@ -404,12 +420,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IllegalMonitorStateException unmatchedUnlockException() {
|
||||||
|
return new IllegalMonitorStateException(
|
||||||
|
"attempt to unlock read lock, not locked by current thread");
|
||||||
|
}
|
||||||
|
|
||||||
protected final int tryAcquireShared(int unused) {
|
protected final int tryAcquireShared(int unused) {
|
||||||
/*
|
/*
|
||||||
* Walkthrough:
|
* Walkthrough:
|
||||||
* 1. If write lock held by another thread, fail.
|
* 1. If write lock held by another thread, fail.
|
||||||
* 2. If count saturated, throw error.
|
* 2. Otherwise, this thread is eligible for
|
||||||
* 3. Otherwise, this thread is eligible for
|
|
||||||
* lock wrt state, so ask if it should block
|
* lock wrt state, so ask if it should block
|
||||||
* because of queue policy. If not, try
|
* because of queue policy. If not, try
|
||||||
* to grant by CASing state and updating count.
|
* to grant by CASing state and updating count.
|
||||||
@ -417,23 +437,33 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
* acquires, which is postponed to full version
|
* acquires, which is postponed to full version
|
||||||
* to avoid having to check hold count in
|
* to avoid having to check hold count in
|
||||||
* the more typical non-reentrant case.
|
* the more typical non-reentrant case.
|
||||||
* 4. If step 3 fails either because thread
|
* 3. If step 2 fails either because thread
|
||||||
* apparently not eligible or CAS fails,
|
* apparently not eligible or CAS fails or count
|
||||||
* chain to version with full retry loop.
|
* saturated, chain to version with full retry loop.
|
||||||
*/
|
*/
|
||||||
Thread current = Thread.currentThread();
|
Thread current = Thread.currentThread();
|
||||||
int c = getState();
|
int c = getState();
|
||||||
if (exclusiveCount(c) != 0 &&
|
if (exclusiveCount(c) != 0 &&
|
||||||
getExclusiveOwnerThread() != current)
|
getExclusiveOwnerThread() != current)
|
||||||
return -1;
|
return -1;
|
||||||
if (sharedCount(c) == MAX_COUNT)
|
int r = sharedCount(c);
|
||||||
throw new Error("Maximum lock count exceeded");
|
|
||||||
if (!readerShouldBlock() &&
|
if (!readerShouldBlock() &&
|
||||||
|
r < MAX_COUNT &&
|
||||||
compareAndSetState(c, c + SHARED_UNIT)) {
|
compareAndSetState(c, c + SHARED_UNIT)) {
|
||||||
HoldCounter rh = cachedHoldCounter;
|
long tid = current.getId();
|
||||||
if (rh == null || rh.tid != current.getId())
|
if (r == 0) {
|
||||||
cachedHoldCounter = rh = readHolds.get();
|
firstReader = tid;
|
||||||
rh.count++;
|
firstReaderHoldCount = 1;
|
||||||
|
} else if (firstReader == tid) {
|
||||||
|
firstReaderHoldCount++;
|
||||||
|
} else {
|
||||||
|
HoldCounter rh = cachedHoldCounter;
|
||||||
|
if (rh == null || rh.tid != tid)
|
||||||
|
cachedHoldCounter = rh = readHolds.get();
|
||||||
|
else if (rh.count == 0)
|
||||||
|
readHolds.set(rh);
|
||||||
|
rh.count++;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return fullTryAcquireShared(current);
|
return fullTryAcquireShared(current);
|
||||||
@ -450,20 +480,56 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
* complicating tryAcquireShared with interactions between
|
* complicating tryAcquireShared with interactions between
|
||||||
* retries and lazily reading hold counts.
|
* retries and lazily reading hold counts.
|
||||||
*/
|
*/
|
||||||
HoldCounter rh = cachedHoldCounter;
|
HoldCounter rh = null;
|
||||||
if (rh == null || rh.tid != current.getId())
|
|
||||||
rh = readHolds.get();
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int c = getState();
|
int c = getState();
|
||||||
int w = exclusiveCount(c);
|
if (exclusiveCount(c) != 0) {
|
||||||
if ((w != 0 && getExclusiveOwnerThread() != current) ||
|
if (getExclusiveOwnerThread() != current)
|
||||||
((rh.count | w) == 0 && readerShouldBlock()))
|
//if (removeNeeded) readHolds.remove();
|
||||||
return -1;
|
return -1;
|
||||||
|
// else we hold the exclusive lock; blocking here
|
||||||
|
// would cause deadlock.
|
||||||
|
} else if (readerShouldBlock()) {
|
||||||
|
// Make sure we're not acquiring read lock reentrantly
|
||||||
|
long tid = current.getId();
|
||||||
|
if (firstReader == tid) {
|
||||||
|
// assert firstReaderHoldCount > 0;
|
||||||
|
} else {
|
||||||
|
if (rh == null) {
|
||||||
|
rh = cachedHoldCounter;
|
||||||
|
if (rh == null || rh.tid != tid) {
|
||||||
|
rh = readHolds.get();
|
||||||
|
if (rh.count == 0)
|
||||||
|
readHolds.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (rh.count == 0)
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (sharedCount(c) == MAX_COUNT)
|
if (sharedCount(c) == MAX_COUNT)
|
||||||
throw new Error("Maximum lock count exceeded");
|
throw new Error("Maximum lock count exceeded");
|
||||||
if (compareAndSetState(c, c + SHARED_UNIT)) {
|
if (compareAndSetState(c, c + SHARED_UNIT)) {
|
||||||
cachedHoldCounter = rh; // cache for release
|
long tid = current.getId();
|
||||||
rh.count++;
|
if (sharedCount(c) == 0) {
|
||||||
|
firstReader = tid;
|
||||||
|
firstReaderHoldCount = 1;
|
||||||
|
} else if (firstReader == tid) {
|
||||||
|
firstReaderHoldCount++;
|
||||||
|
} else {
|
||||||
|
if (rh == null) {
|
||||||
|
rh = cachedHoldCounter;
|
||||||
|
if (rh != null && rh.tid == tid) {
|
||||||
|
if (rh.count == 0)
|
||||||
|
readHolds.set(rh);
|
||||||
|
} else {
|
||||||
|
rh = readHolds.get();
|
||||||
|
}
|
||||||
|
} else if (rh.count == 0)
|
||||||
|
readHolds.set(rh);
|
||||||
|
cachedHoldCounter = rh; // cache for release
|
||||||
|
rh.count++;
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -472,14 +538,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
/**
|
/**
|
||||||
* Performs tryLock for write, enabling barging in both modes.
|
* Performs tryLock for write, enabling barging in both modes.
|
||||||
* This is identical in effect to tryAcquire except for lack
|
* This is identical in effect to tryAcquire except for lack
|
||||||
* of calls to writerShouldBlock
|
* of calls to writerShouldBlock.
|
||||||
*/
|
*/
|
||||||
final boolean tryWriteLock() {
|
final boolean tryWriteLock() {
|
||||||
Thread current = Thread.currentThread();
|
Thread current = Thread.currentThread();
|
||||||
int c = getState();
|
int c = getState();
|
||||||
if (c != 0) {
|
if (c != 0) {
|
||||||
int w = exclusiveCount(c);
|
int w = exclusiveCount(c);
|
||||||
if (w == 0 ||current != getExclusiveOwnerThread())
|
if (w == 0 || current != getExclusiveOwnerThread())
|
||||||
return false;
|
return false;
|
||||||
if (w == MAX_COUNT)
|
if (w == MAX_COUNT)
|
||||||
throw new Error("Maximum lock count exceeded");
|
throw new Error("Maximum lock count exceeded");
|
||||||
@ -493,7 +559,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
/**
|
/**
|
||||||
* Performs tryLock for read, enabling barging in both modes.
|
* Performs tryLock for read, enabling barging in both modes.
|
||||||
* This is identical in effect to tryAcquireShared except for
|
* This is identical in effect to tryAcquireShared except for
|
||||||
* lack of calls to readerShouldBlock
|
* lack of calls to readerShouldBlock.
|
||||||
*/
|
*/
|
||||||
final boolean tryReadLock() {
|
final boolean tryReadLock() {
|
||||||
Thread current = Thread.currentThread();
|
Thread current = Thread.currentThread();
|
||||||
@ -502,13 +568,24 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
if (exclusiveCount(c) != 0 &&
|
if (exclusiveCount(c) != 0 &&
|
||||||
getExclusiveOwnerThread() != current)
|
getExclusiveOwnerThread() != current)
|
||||||
return false;
|
return false;
|
||||||
if (sharedCount(c) == MAX_COUNT)
|
int r = sharedCount(c);
|
||||||
|
if (r == MAX_COUNT)
|
||||||
throw new Error("Maximum lock count exceeded");
|
throw new Error("Maximum lock count exceeded");
|
||||||
if (compareAndSetState(c, c + SHARED_UNIT)) {
|
if (compareAndSetState(c, c + SHARED_UNIT)) {
|
||||||
HoldCounter rh = cachedHoldCounter;
|
long tid = current.getId();
|
||||||
if (rh == null || rh.tid != current.getId())
|
if (r == 0) {
|
||||||
cachedHoldCounter = rh = readHolds.get();
|
firstReader = tid;
|
||||||
rh.count++;
|
firstReaderHoldCount = 1;
|
||||||
|
} else if (firstReader == tid) {
|
||||||
|
firstReaderHoldCount++;
|
||||||
|
} else {
|
||||||
|
HoldCounter rh = cachedHoldCounter;
|
||||||
|
if (rh == null || rh.tid != tid)
|
||||||
|
cachedHoldCounter = rh = readHolds.get();
|
||||||
|
else if (rh.count == 0)
|
||||||
|
readHolds.set(rh);
|
||||||
|
rh.count++;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -546,7 +623,20 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
}
|
}
|
||||||
|
|
||||||
final int getReadHoldCount() {
|
final int getReadHoldCount() {
|
||||||
return getReadLockCount() == 0? 0 : readHolds.get().count;
|
if (getReadLockCount() == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
long tid = Thread.currentThread().getId();
|
||||||
|
if (firstReader == tid)
|
||||||
|
return firstReaderHoldCount;
|
||||||
|
|
||||||
|
HoldCounter rh = cachedHoldCounter;
|
||||||
|
if (rh != null && rh.tid == tid)
|
||||||
|
return rh.count;
|
||||||
|
|
||||||
|
int count = readHolds.get().count;
|
||||||
|
if (count == 0) readHolds.remove();
|
||||||
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -557,6 +647,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
|
|||||||
throws java.io.IOException, ClassNotFoundException {
|
throws java.io.IOException, ClassNotFoundException {
|
||||||
s.defaultReadObject();
|
s.defaultReadObject();
|
||||||
readHolds = new ThreadLocalHoldCounter();
|
readHolds = new ThreadLocalHoldCounter();
|
||||||
|
firstReader = INVALID_THREAD_ID;
|
||||||
setState(0); // reset to unlocked state
|
setState(0); // reset to unlocked state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2005-2006 Sun Microsystems, Inc. All Rights Reserved.
|
* Copyright 2005-2008 Sun Microsystems, Inc. All Rights Reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -23,21 +23,89 @@
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* @test
|
* @test
|
||||||
* @bug 6207928 6328220 6378321
|
* @bug 6207928 6328220 6378321 6625723
|
||||||
* @summary Recursive lock invariant sanity checks
|
* @summary Recursive lock invariant sanity checks
|
||||||
* @author Martin Buchholz
|
* @author Martin Buchholz
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.locks.*;
|
import java.util.concurrent.locks.*;
|
||||||
|
|
||||||
// I am the Cownt, and I lahve to cownt.
|
// I am the Cownt, and I lahve to cownt.
|
||||||
public class Count {
|
public class Count {
|
||||||
private static void realMain(String[] args) throws Throwable {
|
final Random rnd = new Random();
|
||||||
final ReentrantLock rl = new ReentrantLock();
|
|
||||||
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
void lock(Lock lock) {
|
||||||
|
try {
|
||||||
|
switch (rnd.nextInt(4)) {
|
||||||
|
case 0: lock.lock(); break;
|
||||||
|
case 1: lock.lockInterruptibly(); break;
|
||||||
|
case 2: check(lock.tryLock()); break;
|
||||||
|
case 3: check(lock.tryLock(45, TimeUnit.MINUTES)); break;
|
||||||
|
}
|
||||||
|
} catch (Throwable t) { unexpected(t); }
|
||||||
|
}
|
||||||
|
|
||||||
|
void test(String[] args) throws Throwable {
|
||||||
|
for (boolean fair : new boolean[] { true, false })
|
||||||
|
for (boolean serialClone : new boolean[] { true, false }) {
|
||||||
|
testReentrantLocks(fair, serialClone);
|
||||||
|
testConcurrentReadLocks(fair, serialClone);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void testConcurrentReadLocks(final boolean fair,
|
||||||
|
final boolean serialClone) throws Throwable {
|
||||||
|
final int nThreads = 10;
|
||||||
|
final CyclicBarrier barrier = new CyclicBarrier(nThreads);
|
||||||
|
final ExecutorService es = Executors.newFixedThreadPool(nThreads);
|
||||||
|
final ReentrantReadWriteLock rwl = serialClone ?
|
||||||
|
serialClone(new ReentrantReadWriteLock(fair)) :
|
||||||
|
new ReentrantReadWriteLock(fair);
|
||||||
|
for (int i = 0; i < nThreads; i++) {
|
||||||
|
es.submit(new Runnable() { public void run() {
|
||||||
|
try {
|
||||||
|
int n = 5;
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
barrier.await();
|
||||||
|
equal(rwl.getReadHoldCount(), i);
|
||||||
|
equal(rwl.getWriteHoldCount(), 0);
|
||||||
|
check(! rwl.isWriteLocked());
|
||||||
|
equal(rwl.getReadLockCount(), nThreads * i);
|
||||||
|
barrier.await();
|
||||||
|
lock(rwl.readLock());
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
rwl.readLock().unlock();
|
||||||
|
barrier.await();
|
||||||
|
equal(rwl.getReadHoldCount(), n-i-1);
|
||||||
|
equal(rwl.getReadLockCount(), nThreads*(n-i-1));
|
||||||
|
equal(rwl.getWriteHoldCount(), 0);
|
||||||
|
check(! rwl.isWriteLocked());
|
||||||
|
barrier.await();
|
||||||
|
}
|
||||||
|
THROWS(IllegalMonitorStateException.class,
|
||||||
|
new F(){void f(){rwl.readLock().unlock();}},
|
||||||
|
new F(){void f(){rwl.writeLock().unlock();}});
|
||||||
|
barrier.await();
|
||||||
|
} catch (Throwable t) { unexpected(t); }}});}
|
||||||
|
es.shutdown();
|
||||||
|
check(es.awaitTermination(10, TimeUnit.SECONDS));
|
||||||
|
}
|
||||||
|
|
||||||
|
void testReentrantLocks(final boolean fair,
|
||||||
|
final boolean serialClone) throws Throwable {
|
||||||
|
final ReentrantLock rl = serialClone ?
|
||||||
|
serialClone(new ReentrantLock(fair)) :
|
||||||
|
new ReentrantLock(fair);
|
||||||
|
final ReentrantReadWriteLock rwl = serialClone ?
|
||||||
|
serialClone(new ReentrantReadWriteLock(fair)) :
|
||||||
|
new ReentrantReadWriteLock(fair);
|
||||||
final int depth = 10;
|
final int depth = 10;
|
||||||
|
equal(rl.isFair(), fair);
|
||||||
|
equal(rwl.isFair(), fair);
|
||||||
check(! rl.isLocked());
|
check(! rl.isLocked());
|
||||||
check(! rwl.isWriteLocked());
|
check(! rwl.isWriteLocked());
|
||||||
check(! rl.isHeldByCurrentThread());
|
check(! rl.isHeldByCurrentThread());
|
||||||
@ -50,28 +118,11 @@ public class Count {
|
|||||||
equal(rwl.getReadHoldCount(), i);
|
equal(rwl.getReadHoldCount(), i);
|
||||||
equal(rwl.getWriteHoldCount(), i);
|
equal(rwl.getWriteHoldCount(), i);
|
||||||
equal(rwl.writeLock().getHoldCount(), i);
|
equal(rwl.writeLock().getHoldCount(), i);
|
||||||
switch (i%4) {
|
equal(rl.isLocked(), i > 0);
|
||||||
case 0:
|
equal(rwl.isWriteLocked(), i > 0);
|
||||||
rl.lock();
|
lock(rl);
|
||||||
rwl.writeLock().lock();
|
lock(rwl.writeLock());
|
||||||
rwl.readLock().lock();
|
lock(rwl.readLock());
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
rl.lockInterruptibly();
|
|
||||||
rwl.writeLock().lockInterruptibly();
|
|
||||||
rwl.readLock().lockInterruptibly();
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
check(rl.tryLock());
|
|
||||||
check(rwl.writeLock().tryLock());
|
|
||||||
check(rwl.readLock().tryLock());
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
check(rl.tryLock(454, TimeUnit.MILLISECONDS));
|
|
||||||
check(rwl.writeLock().tryLock(454, TimeUnit.NANOSECONDS));
|
|
||||||
check(rwl.readLock().tryLock(454, TimeUnit.HOURS));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = depth; i > 0; i--) {
|
for (int i = depth; i > 0; i--) {
|
||||||
@ -95,20 +146,48 @@ public class Count {
|
|||||||
rwl.writeLock().unlock();
|
rwl.writeLock().unlock();
|
||||||
rl.unlock();
|
rl.unlock();
|
||||||
}
|
}
|
||||||
|
THROWS(IllegalMonitorStateException.class,
|
||||||
|
new F(){void f(){rl.unlock();}},
|
||||||
|
new F(){void f(){rwl.readLock().unlock();}},
|
||||||
|
new F(){void f(){rwl.writeLock().unlock();}});
|
||||||
}
|
}
|
||||||
|
|
||||||
//--------------------- Infrastructure ---------------------------
|
//--------------------- Infrastructure ---------------------------
|
||||||
static volatile int passed = 0, failed = 0;
|
volatile int passed = 0, failed = 0;
|
||||||
static void pass() {passed++;}
|
void pass() {passed++;}
|
||||||
static void fail() {failed++; Thread.dumpStack();}
|
void fail() {failed++; Thread.dumpStack();}
|
||||||
static void fail(String msg) {System.out.println(msg); fail();}
|
void fail(String msg) {System.err.println(msg); fail();}
|
||||||
static void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
void unexpected(Throwable t) {failed++; t.printStackTrace();}
|
||||||
static void check(boolean cond) {if (cond) pass(); else fail();}
|
void check(boolean cond) {if (cond) pass(); else fail();}
|
||||||
static void equal(Object x, Object y) {
|
void equal(Object x, Object y) {
|
||||||
if (x == null ? y == null : x.equals(y)) pass();
|
if (x == null ? y == null : x.equals(y)) pass();
|
||||||
else fail(x + " not equal to " + y);}
|
else fail(x + " not equal to " + y);}
|
||||||
public static void main(String[] args) throws Throwable {
|
public static void main(String[] args) throws Throwable {
|
||||||
try {realMain(args);} catch (Throwable t) {unexpected(t);}
|
new Count().instanceMain(args);}
|
||||||
|
void instanceMain(String[] args) throws Throwable {
|
||||||
|
try {test(args);} catch (Throwable t) {unexpected(t);}
|
||||||
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
System.out.printf("%nPassed = %d, failed = %d%n%n", passed, failed);
|
||||||
if (failed > 0) throw new AssertionError("Some tests failed");}
|
if (failed > 0) throw new AssertionError("Some tests failed");}
|
||||||
|
abstract class F {abstract void f() throws Throwable;}
|
||||||
|
void THROWS(Class<? extends Throwable> k, F... fs) {
|
||||||
|
for (F f : fs)
|
||||||
|
try {f.f(); fail("Expected " + k.getName() + " not thrown");}
|
||||||
|
catch (Throwable t) {
|
||||||
|
if (k.isAssignableFrom(t.getClass())) pass();
|
||||||
|
else unexpected(t);}}
|
||||||
|
|
||||||
|
static byte[] serializedForm(Object obj) {
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
new ObjectOutputStream(baos).writeObject(obj);
|
||||||
|
return baos.toByteArray();
|
||||||
|
} catch (IOException e) { throw new RuntimeException(e); }}
|
||||||
|
static Object readObject(byte[] bytes)
|
||||||
|
throws IOException, ClassNotFoundException {
|
||||||
|
InputStream is = new ByteArrayInputStream(bytes);
|
||||||
|
return new ObjectInputStream(is).readObject();}
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <T> T serialClone(T obj) {
|
||||||
|
try { return (T) readObject(serializedForm(obj)); }
|
||||||
|
catch (Exception e) { throw new RuntimeException(e); }}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user