8187941: Add StampedLock stamp inspection methods

Reviewed-by: martin, psandoz
This commit is contained in:
Doug Lea 2017-10-13 18:29:21 -07:00
parent f8ae408aa9
commit dc895e3911
2 changed files with 243 additions and 15 deletions
src/java.base/share/classes/java/util/concurrent/locks
test/jdk/java/util/concurrent/tck

@ -140,7 +140,8 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
* private double x, y;
* private final StampedLock sl = new StampedLock();
*
* void move(double deltaX, double deltaY) { // an exclusively locked method
* // an exclusively locked method
* void move(double deltaX, double deltaY) {
* long stamp = sl.writeLock();
* try {
* x += deltaX;
@ -150,25 +151,57 @@ import jdk.internal.vm.annotation.ReservedStackAccess;
* }
* }
*
* double distanceFromOrigin() { // A read-only method
* double currentX, currentY;
* // a read-only method
* // upgrade from optimistic read to read lock
* double distanceFromOrigin() {
* long stamp = sl.tryOptimisticRead();
* do {
* if (stamp == 0L)
* stamp = sl.readLock();
* try {
* try {
* retryHoldingLock: for (;; stamp = sl.readLock()) {
* if (stamp == 0L)
* continue retryHoldingLock;
* // possibly racy reads
* currentX = x;
* currentY = y;
* } finally {
* stamp = sl.tryConvertToOptimisticRead(stamp);
* double currentX = x;
* double currentY = y;
* if (!sl.validate(stamp))
* continue retryHoldingLock;
* return Math.hypot(currentX, currentY);
* }
* } while (stamp == 0);
* return Math.hypot(currentX, currentY);
* } finally {
* if (StampedLock.isReadLockStamp(stamp))
* sl.unlockRead(stamp);
* }
* }
*
* void moveIfAtOrigin(double newX, double newY) { // upgrade
* // Could instead start with optimistic, not read mode
* // upgrade from optimistic read to write lock
* void moveIfAtOrigin(double newX, double newY) {
* long stamp = sl.tryOptimisticRead();
* try {
* retryHoldingLock: for (;; stamp = sl.writeLock()) {
* if (stamp == 0L)
* continue retryHoldingLock;
* // possibly racy reads
* double currentX = x;
* double currentY = y;
* if (!sl.validate(stamp))
* continue retryHoldingLock;
* if (currentX != 0.0 || currentY != 0.0)
* break;
* stamp = sl.tryConvertToWriteLock(stamp);
* if (stamp == 0L)
* continue retryHoldingLock;
* // exclusive access
* x = newX;
* y = newY;
* return;
* }
* } finally {
* if (StampedLock.isWriteLockStamp(stamp))
* sl.unlockWrite(stamp);
* }
* }
*
* // Upgrade read lock to write lock
* void moveIfAtOrigin(double newX, double newY) {
* long stamp = sl.readLock();
* try {
* while (x == 0.0 && y == 0.0) {
@ -881,6 +914,92 @@ public class StampedLock implements java.io.Serializable {
return (state & RBITS) != 0L;
}
/**
* Tells whether a stamp represents holding a lock exclusively.
* This method may be useful in conjunction with
* {@link #tryConvertToWriteLock}, for example: <pre> {@code
* long stamp = sl.tryOptimisticRead();
* try {
* ...
* stamp = sl.tryConvertToWriteLock(stamp);
* ...
* } finally {
* if (StampedLock.isWriteLockStamp(stamp))
* sl.unlockWrite(stamp);
* }}</pre>
*
* @param stamp a stamp returned by a previous StampedLock operation
* @return {@code true} if the stamp was returned by a successful
* write-lock operation
* @since 10
*/
public static boolean isWriteLockStamp(long stamp) {
return (stamp & ABITS) == WBIT;
}
/**
* Tells whether a stamp represents holding a lock non-exclusively.
* This method may be useful in conjunction with
* {@link #tryConvertToReadLock}, for example: <pre> {@code
* long stamp = sl.tryOptimisticRead();
* try {
* ...
* stamp = sl.tryConvertToReadLock(stamp);
* ...
* } finally {
* if (StampedLock.isReadLockStamp(stamp))
* sl.unlockRead(stamp);
* }}</pre>
*
* @param stamp a stamp returned by a previous StampedLock operation
* @return {@code true} if the stamp was returned by a successful
* read-lock operation
* @since 10
*/
public static boolean isReadLockStamp(long stamp) {
return (stamp & RBITS) != 0L;
}
/**
* Tells whether a stamp represents holding a lock.
* This method may be useful in conjunction with
* {@link #tryConvertToReadLock} and {@link #tryConvertToWriteLock},
* for example: <pre> {@code
* long stamp = sl.tryOptimisticRead();
* try {
* ...
* stamp = sl.tryConvertToReadLock(stamp);
* ...
* stamp = sl.tryConvertToWriteLock(stamp);
* ...
* } finally {
* if (StampedLock.isLockStamp(stamp))
* sl.unlock(stamp);
* }}</pre>
*
* @param stamp a stamp returned by a previous StampedLock operation
* @return {@code true} if the stamp was returned by a successful
* read-lock or write-lock operation
* @since 10
*/
public static boolean isLockStamp(long stamp) {
return (stamp & ABITS) != 0L;
}
/**
* Tells whether a stamp represents a successful optimistic read.
*
* @param stamp a stamp returned by a previous StampedLock operation
* @return {@code true} if the stamp was returned by a successful
* optimistic read operation, that is, a non-zero return from
* {@link #tryOptimisticRead()} or
* {@link #tryConvertToOptimisticRead(long)}
* @since 10
*/
public static boolean isOptimisticReadStamp(long stamp) {
return (stamp & ABITS) == 0L && stamp != 0L;
}
/**
* Queries the number of read locks held for this lock. This
* method is designed for use in monitoring system state, not for

@ -35,6 +35,11 @@
import static java.util.concurrent.TimeUnit.DAYS;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.locks.StampedLock.isLockStamp;
import static java.util.concurrent.locks.StampedLock.isOptimisticReadStamp;
import static java.util.concurrent.locks.StampedLock.isReadLockStamp;
import static java.util.concurrent.locks.StampedLock.isWriteLockStamp;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
@ -1286,11 +1291,115 @@ public class StampedLockTest extends JSR166TestCase {
} while (stamp == 0);
return Math.hypot(currentX, currentY);
}
double distanceFromOrigin2() {
long stamp = sl.tryOptimisticRead();
try {
retryHoldingLock:
for (;; stamp = sl.readLock()) {
if (stamp == 0L)
continue retryHoldingLock;
// possibly racy reads
double currentX = x;
double currentY = y;
if (!sl.validate(stamp))
continue retryHoldingLock;
return Math.hypot(currentX, currentY);
}
} finally {
if (StampedLock.isReadLockStamp(stamp))
sl.unlockRead(stamp);
}
}
void moveIfAtOrigin(double newX, double newY) {
long stamp = sl.readLock();
try {
while (x == 0.0 && y == 0.0) {
long ws = sl.tryConvertToWriteLock(stamp);
if (ws != 0L) {
stamp = ws;
x = newX;
y = newY;
return;
}
else {
sl.unlockRead(stamp);
stamp = sl.writeLock();
}
}
} finally {
sl.unlock(stamp);
}
}
}
Point p = new Point();
p.move(3.0, 4.0);
assertEquals(5.0, p.distanceFromOrigin());
p.moveIfAtOrigin(5.0, 12.0);
assertEquals(5.0, p.distanceFromOrigin2());
}
/**
* Stamp inspection methods work as expected, and do not inspect
* the state of the lock itself.
*/
public void testStampStateInspectionMethods() {
StampedLock lock = new StampedLock();
assertFalse(isWriteLockStamp(0L));
assertFalse(isReadLockStamp(0L));
assertFalse(isLockStamp(0L));
assertFalse(isOptimisticReadStamp(0L));
{
long stamp = lock.writeLock();
for (int i = 0; i < 2; i++) {
assertTrue(isWriteLockStamp(stamp));
assertFalse(isReadLockStamp(stamp));
assertTrue(isLockStamp(stamp));
assertFalse(isOptimisticReadStamp(stamp));
if (i == 0)
lock.unlockWrite(stamp);
}
}
{
long stamp = lock.readLock();
for (int i = 0; i < 2; i++) {
assertFalse(isWriteLockStamp(stamp));
assertTrue(isReadLockStamp(stamp));
assertTrue(isLockStamp(stamp));
assertFalse(isOptimisticReadStamp(stamp));
if (i == 0)
lock.unlockRead(stamp);
}
}
{
long optimisticStamp = lock.tryOptimisticRead();
long readStamp = lock.tryConvertToReadLock(optimisticStamp);
long writeStamp = lock.tryConvertToWriteLock(readStamp);
for (int i = 0; i < 2; i++) {
assertFalse(isWriteLockStamp(optimisticStamp));
assertFalse(isReadLockStamp(optimisticStamp));
assertFalse(isLockStamp(optimisticStamp));
assertTrue(isOptimisticReadStamp(optimisticStamp));
assertFalse(isWriteLockStamp(readStamp));
assertTrue(isReadLockStamp(readStamp));
assertTrue(isLockStamp(readStamp));
assertFalse(isOptimisticReadStamp(readStamp));
assertTrue(isWriteLockStamp(writeStamp));
assertFalse(isReadLockStamp(writeStamp));
assertTrue(isLockStamp(writeStamp));
assertFalse(isOptimisticReadStamp(writeStamp));
if (i == 0)
lock.unlockWrite(writeStamp);
}
}
}
}