diff --git a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java index 0ff5770391c..36a8c5a9849 100644 --- a/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java +++ b/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java @@ -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:
{@code
+ * long stamp = sl.tryOptimisticRead();
+ * try {
+ * ...
+ * stamp = sl.tryConvertToWriteLock(stamp);
+ * ...
+ * } finally {
+ * if (StampedLock.isWriteLockStamp(stamp))
+ * sl.unlockWrite(stamp);
+ * }}
+ *
+ * @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: {@code
+ * long stamp = sl.tryOptimisticRead();
+ * try {
+ * ...
+ * stamp = sl.tryConvertToReadLock(stamp);
+ * ...
+ * } finally {
+ * if (StampedLock.isReadLockStamp(stamp))
+ * sl.unlockRead(stamp);
+ * }}
+ *
+ * @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: {@code
+ * long stamp = sl.tryOptimisticRead();
+ * try {
+ * ...
+ * stamp = sl.tryConvertToReadLock(stamp);
+ * ...
+ * stamp = sl.tryConvertToWriteLock(stamp);
+ * ...
+ * } finally {
+ * if (StampedLock.isLockStamp(stamp))
+ * sl.unlock(stamp);
+ * }}
+ *
+ * @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
diff --git a/test/jdk/java/util/concurrent/tck/StampedLockTest.java b/test/jdk/java/util/concurrent/tck/StampedLockTest.java
index 305bfdb5380..e415d470c99 100644
--- a/test/jdk/java/util/concurrent/tck/StampedLockTest.java
+++ b/test/jdk/java/util/concurrent/tck/StampedLockTest.java
@@ -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);
+ }
+ }
}
}