6822903: Reliability and documentation improvements for ReentrantReadWriteLock
Make firstReader a Thread, not a long Reviewed-by: martin
This commit is contained in:
parent
b63d6d68d9
commit
ec207ef682
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user