# HG changeset patch # User dl # Date 1507944561 25200 # Node ID 75ee0b48ea6318c735fa7dcd5a6b4c0cef7648bf # Parent bffcbf07ea88f01dc58229f8de05476fe7affb05 8187941: Add StampedLock stamp inspection methods Reviewed-by: martin, psandoz diff -r bffcbf07ea88 -r 75ee0b48ea63 src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java --- 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:
 {@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 * synchronization control. diff -r bffcbf07ea88 -r 75ee0b48ea63 test/jdk/java/util/concurrent/tck/StampedLockTest.java --- 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); + } + } } }