6822903: Reliability and documentation improvements for ReentrantReadWriteLock

Make firstReader a Thread, not a long

Reviewed-by: martin
This commit is contained in:
Doug Lea 2009-03-26 17:39:42 -07:00
parent b63d6d68d9
commit ec207ef682

View File

@ -276,7 +276,7 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* Maintained as a ThreadLocal; cached in cachedHoldCounter * Maintained as a ThreadLocal; cached in cachedHoldCounter
*/ */
static final class HoldCounter { static final class HoldCounter {
int count; int count = 0;
// 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();
} }
@ -293,8 +293,9 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
} }
/** /**
* The number of read locks held by current thread. * The number of reentrant read locks held by current thread.
* Initialized only in constructor and readObject. * Initialized only in constructor and readObject.
* Removed whenever a thread's read hold count drops to 0.
*/ */
private transient ThreadLocalHoldCounter readHolds; private transient ThreadLocalHoldCounter readHolds;
@ -304,17 +305,35 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
* where the next thread to release is the last one to * where the next thread to release is the last one to
* 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.
*
* <p>Can outlive the Thread for which it is caching the read
* hold count, but avoids garbage retention by not retaining a
* reference to the Thread.
*
* <p>Accessed via a benign data race; relies on the memory
* model's final field and out-of-thin-air guarantees.
*/ */
private transient HoldCounter cachedHoldCounter; private transient HoldCounter cachedHoldCounter;
/** /**
* firstReader is the first thread to have acquired the read lock. * firstReader is the first thread to have acquired the read lock.
* firstReaderHoldCount is firstReader's hold count. * firstReaderHoldCount is firstReader's hold count.
* This allows tracking of read holds for uncontended read *
* <p>More precisely, firstReader is the unique thread that last
* changed the shared count from 0 to 1, and has not released the
* read lock since then; null if there is no such thread.
*
* <p>Cannot cause garbage retention unless the thread terminated
* without relinquishing its read locks, since tryReleaseShared
* sets it to null.
*
* <p>Accessed via a benign data race; relies on the memory
* model's out-of-thin-air guarantees for references.
*
* <p>This allows tracking of read holds for uncontended read
* locks to be very cheap. * locks to be very cheap.
*/ */
private final static long INVALID_THREAD_ID = -1; private transient Thread firstReader = null;
private transient long firstReader = INVALID_THREAD_ID;
private transient int firstReaderHoldCount; private transient int firstReaderHoldCount;
Sync() { Sync() {
@ -393,16 +412,16 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
} }
protected final boolean tryReleaseShared(int unused) { protected final boolean tryReleaseShared(int unused) {
long tid = Thread.currentThread().getId(); Thread current = Thread.currentThread();
if (firstReader == tid) { if (firstReader == current) {
// assert firstReaderHoldCount > 0; // assert firstReaderHoldCount > 0;
if (firstReaderHoldCount == 1) if (firstReaderHoldCount == 1)
firstReader = INVALID_THREAD_ID; firstReader = null;
else else
firstReaderHoldCount--; firstReaderHoldCount--;
} else { } else {
HoldCounter rh = cachedHoldCounter; HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != tid) if (rh == null || rh.tid != current.getId())
rh = readHolds.get(); rh = readHolds.get();
int count = rh.count; int count = rh.count;
if (count <= 1) { if (count <= 1) {
@ -416,6 +435,9 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
int c = getState(); int c = getState();
int nextc = c - SHARED_UNIT; int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc)) if (compareAndSetState(c, nextc))
// Releasing the read lock has no effect on readers,
// but it may allow waiting writers to proceed if
// both read and write locks are now free.
return nextc == 0; return nextc == 0;
} }
} }
@ -450,15 +472,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
if (!readerShouldBlock() && if (!readerShouldBlock() &&
r < MAX_COUNT && r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) { compareAndSetState(c, c + SHARED_UNIT)) {
long tid = current.getId();
if (r == 0) { if (r == 0) {
firstReader = tid; firstReader = current;
firstReaderHoldCount = 1; firstReaderHoldCount = 1;
} else if (firstReader == tid) { } else if (firstReader == current) {
firstReaderHoldCount++; firstReaderHoldCount++;
} else { } else {
HoldCounter rh = cachedHoldCounter; HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != tid) if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get(); cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0) else if (rh.count == 0)
readHolds.set(rh); readHolds.set(rh);
@ -485,19 +506,17 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
int c = getState(); int c = getState();
if (exclusiveCount(c) != 0) { if (exclusiveCount(c) != 0) {
if (getExclusiveOwnerThread() != current) if (getExclusiveOwnerThread() != current)
//if (removeNeeded) readHolds.remove();
return -1; return -1;
// else we hold the exclusive lock; blocking here // else we hold the exclusive lock; blocking here
// would cause deadlock. // would cause deadlock.
} else if (readerShouldBlock()) { } else if (readerShouldBlock()) {
// Make sure we're not acquiring read lock reentrantly // Make sure we're not acquiring read lock reentrantly
long tid = current.getId(); if (firstReader == current) {
if (firstReader == tid) {
// assert firstReaderHoldCount > 0; // assert firstReaderHoldCount > 0;
} else { } else {
if (rh == null) { if (rh == null) {
rh = cachedHoldCounter; rh = cachedHoldCounter;
if (rh == null || rh.tid != tid) { if (rh == null || rh.tid != current.getId()) {
rh = readHolds.get(); rh = readHolds.get();
if (rh.count == 0) if (rh.count == 0)
readHolds.remove(); readHolds.remove();
@ -510,25 +529,20 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
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)) {
long tid = current.getId();
if (sharedCount(c) == 0) { if (sharedCount(c) == 0) {
firstReader = tid; firstReader = current;
firstReaderHoldCount = 1; firstReaderHoldCount = 1;
} else if (firstReader == tid) { } else if (firstReader == current) {
firstReaderHoldCount++; firstReaderHoldCount++;
} else { } else {
if (rh == null) { if (rh == null)
rh = cachedHoldCounter; rh = cachedHoldCounter;
if (rh != null && rh.tid == tid) { if (rh == null || rh.tid != current.getId())
if (rh.count == 0) rh = readHolds.get();
readHolds.set(rh); else if (rh.count == 0)
} else {
rh = readHolds.get();
}
} else if (rh.count == 0)
readHolds.set(rh); readHolds.set(rh);
cachedHoldCounter = rh; // cache for release
rh.count++; rh.count++;
cachedHoldCounter = rh; // cache for release
} }
return 1; return 1;
} }
@ -572,15 +586,14 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
if (r == MAX_COUNT) 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)) {
long tid = current.getId();
if (r == 0) { if (r == 0) {
firstReader = tid; firstReader = current;
firstReaderHoldCount = 1; firstReaderHoldCount = 1;
} else if (firstReader == tid) { } else if (firstReader == current) {
firstReaderHoldCount++; firstReaderHoldCount++;
} else { } else {
HoldCounter rh = cachedHoldCounter; HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != tid) if (rh == null || rh.tid != current.getId())
cachedHoldCounter = rh = readHolds.get(); cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0) else if (rh.count == 0)
readHolds.set(rh); readHolds.set(rh);
@ -626,12 +639,12 @@ public class ReentrantReadWriteLock implements ReadWriteLock, java.io.Serializab
if (getReadLockCount() == 0) if (getReadLockCount() == 0)
return 0; return 0;
long tid = Thread.currentThread().getId(); Thread current = Thread.currentThread();
if (firstReader == tid) if (firstReader == current)
return firstReaderHoldCount; return firstReaderHoldCount;
HoldCounter rh = cachedHoldCounter; HoldCounter rh = cachedHoldCounter;
if (rh != null && rh.tid == tid) if (rh != null && rh.tid == current.getId())
return rh.count; return rh.count;
int count = readHolds.get().count; int count = readHolds.get().count;
@ -647,7 +660,6 @@ 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
} }