--- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Fri Oct 13 18:19:18 2017 -0700
+++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Fri Oct 13 18:29:21 2017 -0700
@@ -140,7 +140,8 @@
* 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 @@
* }
* }
*
- * 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) {
@@ -882,6 +915,92 @@
}
/**
+ * 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
* synchronization control.
--- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java Fri Oct 13 18:19:18 2017 -0700
+++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java Fri Oct 13 18:29:21 2017 -0700
@@ -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 @@
} 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);
+ }
+ }
}
}