# HG changeset patch # User dl # Date 1468615903 25200 # Node ID a0b0fa5763b169adae2762b68e39f71480913600 # Parent 5bf88dce615f6804f9e101a96ffa7c9dfb4fbbbe 8159924: Various improvements to StampedLock code Reviewed-by: martin, psandoz, rriggs, plevart, dfuchs diff -r 5bf88dce615f -r a0b0fa5763b1 jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java --- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Wed Jul 05 21:58:29 2017 +0200 +++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java Fri Jul 15 13:51:43 2016 -0700 @@ -35,7 +35,10 @@ package java.util.concurrent.locks; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.VarHandle; import java.util.concurrent.TimeUnit; +import jdk.internal.vm.annotation.ReservedStackAccess; /** * A capability-based lock with three modes for controlling read/write @@ -108,6 +111,10 @@ * into initial unlocked state, so they are not useful for remote * locking. * + *
Like {@link java.util.concurrent.Semaphore Semaphore}, but unlike most + * {@link Lock} implementations, StampedLocks have no notion of ownership. + * Locks acquired in one thread can be released or converted in another. + * *
The scheduling policy of StampedLock does not consistently
* prefer readers over writers or vice versa. All "try" methods are
* best-effort and do not necessarily conform to any scheduling or
@@ -126,7 +133,7 @@
* in a class that maintains simple two-dimensional points. The sample
* code illustrates some try/catch conventions even though they are
* not strictly needed here because no exceptions can occur in their
- * bodies.
+ * bodies.
*
*
{@code * class Point { @@ -234,9 +241,7 @@ * used in the acquire methods to reduce (increasingly expensive) * context switching while also avoiding sustained memory * thrashing among many threads. We limit spins to the head of - * queue. A thread spin-waits up to SPINS times (where each - * iteration decreases spin count with 50% probability) before - * blocking. If, upon wakening it fails to obtain lock, and is + * queue. If, upon wakening, a thread fails to obtain lock, and is * still (or becomes) the first waiting thread (which indicates * that some other thread barged and obtained lock), it escalates * spins (up to MAX_HEAD_SPINS) to reduce the likelihood of @@ -252,7 +257,7 @@ * to normal volatile reads (of "state"). To force orderings of * reads before a validation and the validation itself in those * cases where this is not already forced, we use - * Unsafe.loadFence. + * VarHandle.acquireFence. * * The memory layout keeps lock state and queue pointers together * (normally on the same cache line). This usually works well for @@ -290,7 +295,20 @@ private static final long ABITS = RBITS | WBIT; private static final long SBITS = ~RBITS; // note overlap with ABITS - // Initial value for lock state; avoid failure value zero + /* + * 3 stamp modes can be distinguished by examining (m = stamp & ABITS): + * write mode: m == WBIT + * optimistic read mode: m == 0L (even when read lock is held) + * read mode: m > 0L && m <= RFULL (the stamp is a copy of state, but the + * read hold count in the stamp is unused other than to determine mode) + * + * This differs slightly from the encoding of state: + * (state & ABITS) == 0L indicates the lock is currently unlocked. + * (state & ABITS) == RBITS is a special transient value + * indicating spin-locked to manipulate reader bits overflow. + */ + + /** Initial value for lock state; avoids failure value zero. */ private static final long ORIGIN = WBIT << 1; // Special value from cancelled acquire methods so caller can throw IE @@ -341,25 +359,27 @@ * Exclusively acquires the lock, blocking if necessary * until available. * - * @return a stamp that can be used to unlock or convert mode + * @return a write stamp that can be used to unlock or convert mode */ + @ReservedStackAccess public long writeLock() { long s, next; // bypass acquireWrite in fully unlocked case only return ((((s = state) & ABITS) == 0L && - U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? + STATE.compareAndSet(this, s, next = s + WBIT)) ? next : acquireWrite(false, 0L)); } /** * Exclusively acquires the lock if it is immediately available. * - * @return a stamp that can be used to unlock or convert mode, + * @return a write stamp that can be used to unlock or convert mode, * or zero if the lock is not available */ + @ReservedStackAccess public long tryWriteLock() { long s, next; return ((((s = state) & ABITS) == 0L && - U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ? + STATE.compareAndSet(this, s, next = s + WBIT)) ? next : 0L); } @@ -371,7 +391,7 @@ * * @param time the maximum time to wait for the lock * @param unit the time unit of the {@code time} argument - * @return a stamp that can be used to unlock or convert mode, + * @return a write stamp that can be used to unlock or convert mode, * or zero if the lock is not available * @throws InterruptedException if the current thread is interrupted * before acquiring the lock @@ -399,10 +419,11 @@ * Behavior under interruption matches that specified * for method {@link Lock#lockInterruptibly()}. * - * @return a stamp that can be used to unlock or convert mode + * @return a write stamp that can be used to unlock or convert mode * @throws InterruptedException if the current thread is interrupted * before acquiring the lock */ + @ReservedStackAccess public long writeLockInterruptibly() throws InterruptedException { long next; if (!Thread.interrupted() && @@ -415,33 +436,34 @@ * Non-exclusively acquires the lock, blocking if necessary * until available. * - * @return a stamp that can be used to unlock or convert mode + * @return a read stamp that can be used to unlock or convert mode */ + @ReservedStackAccess public long readLock() { long s = state, next; // bypass acquireRead on common uncontended case return ((whead == wtail && (s & ABITS) < RFULL && - U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ? + STATE.compareAndSet(this, s, next = s + RUNIT)) ? next : acquireRead(false, 0L)); } /** * Non-exclusively acquires the lock if it is immediately available. * - * @return a stamp that can be used to unlock or convert mode, + * @return a read stamp that can be used to unlock or convert mode, * or zero if the lock is not available */ + @ReservedStackAccess public long tryReadLock() { - for (;;) { - long s, m, next; - if ((m = (s = state) & ABITS) == WBIT) - return 0L; - else if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + long s, m, next; + while ((m = (s = state) & ABITS) != WBIT) { + if (m < RFULL) { + if (STATE.compareAndSet(this, s, next = s + RUNIT)) return next; } else if ((next = tryIncReaderOverflow(s)) != 0L) return next; } + return 0L; } /** @@ -452,11 +474,12 @@ * * @param time the maximum time to wait for the lock * @param unit the time unit of the {@code time} argument - * @return a stamp that can be used to unlock or convert mode, + * @return a read stamp that can be used to unlock or convert mode, * or zero if the lock is not available * @throws InterruptedException if the current thread is interrupted * before acquiring the lock */ + @ReservedStackAccess public long tryReadLock(long time, TimeUnit unit) throws InterruptedException { long s, m, next, deadline; @@ -464,7 +487,7 @@ if (!Thread.interrupted()) { if ((m = (s = state) & ABITS) != WBIT) { if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + if (STATE.compareAndSet(this, s, next = s + RUNIT)) return next; } else if ((next = tryIncReaderOverflow(s)) != 0L) @@ -486,10 +509,11 @@ * Behavior under interruption matches that specified * for method {@link Lock#lockInterruptibly()}. * - * @return a stamp that can be used to unlock or convert mode + * @return a read stamp that can be used to unlock or convert mode * @throws InterruptedException if the current thread is interrupted * before acquiring the lock */ + @ReservedStackAccess public long readLockInterruptibly() throws InterruptedException { long next; if (!Thread.interrupted() && @@ -502,7 +526,7 @@ * Returns a stamp that can later be validated, or zero * if exclusively locked. * - * @return a stamp, or zero if exclusively locked + * @return a valid optimistic read stamp, or zero if exclusively locked */ public long tryOptimisticRead() { long s; @@ -522,11 +546,29 @@ * since issuance of the given stamp; else false */ public boolean validate(long stamp) { - U.loadFence(); + VarHandle.acquireFence(); return (stamp & SBITS) == (state & SBITS); } /** + * Returns an unlocked state, incrementing the version and + * avoiding special failure value 0L. + * + * @param s a write-locked state (or stamp) + */ + private static long unlockWriteState(long s) { + return ((s += WBIT) == 0L) ? ORIGIN : s; + } + + private long unlockWriteInternal(long s) { + long next; WNode h; + STATE.setVolatile(this, next = unlockWriteState(s)); + if ((h = whead) != null && h.status != 0) + release(h); + return next; + } + + /** * If the lock state matches the given stamp, releases the * exclusive lock. * @@ -534,13 +576,11 @@ * @throws IllegalMonitorStateException if the stamp does * not match the current state of this lock */ + @ReservedStackAccess public void unlockWrite(long stamp) { - WNode h; if (state != stamp || (stamp & WBIT) == 0L) throw new IllegalMonitorStateException(); - U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp); - if ((h = whead) != null && h.status != 0) - release(h); + unlockWriteInternal(stamp); } /** @@ -551,22 +591,23 @@ * @throws IllegalMonitorStateException if the stamp does * not match the current state of this lock */ + @ReservedStackAccess public void unlockRead(long stamp) { long s, m; WNode h; - for (;;) { - if (((s = state) & SBITS) != (stamp & SBITS) || - (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT) - throw new IllegalMonitorStateException(); + while (((s = state) & SBITS) == (stamp & SBITS) + && (stamp & RBITS) > 0L + && ((m = s & RBITS) > 0L)) { if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if (STATE.compareAndSet(this, s, s - RUNIT)) { if (m == RUNIT && (h = whead) != null && h.status != 0) release(h); - break; + return; } } else if (tryDecReaderOverflow(s) != 0L) - break; + return; } + throw new IllegalMonitorStateException(); } /** @@ -577,32 +618,12 @@ * @throws IllegalMonitorStateException if the stamp does * not match the current state of this lock */ + @ReservedStackAccess public void unlock(long stamp) { - long a = stamp & ABITS, m, s; WNode h; - while (((s = state) & SBITS) == (stamp & SBITS)) { - if ((m = s & ABITS) == 0L) - break; - else if (m == WBIT) { - if (a != m) - break; - U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s); - if ((h = whead) != null && h.status != 0) - release(h); - return; - } - else if (a == 0L || a >= WBIT) - break; - else if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { - if (m == RUNIT && (h = whead) != null && h.status != 0) - release(h); - return; - } - } - else if (tryDecReaderOverflow(s) != 0L) - return; - } - throw new IllegalMonitorStateException(); + if ((stamp & WBIT) != 0) + unlockWrite(stamp); + else + unlockRead(stamp); } /** @@ -623,7 +644,7 @@ if ((m = s & ABITS) == 0L) { if (a != 0L) break; - if (U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) + if (STATE.compareAndSet(this, s, next = s + WBIT)) return next; } else if (m == WBIT) { @@ -632,8 +653,7 @@ return stamp; } else if (m == RUNIT && a != 0L) { - if (U.compareAndSwapLong(this, STATE, s, - next = s - RUNIT + WBIT)) + if (STATE.compareAndSet(this, s, next = s - RUNIT + WBIT)) return next; } else @@ -654,30 +674,32 @@ * @return a valid read stamp, or zero on failure */ public long tryConvertToReadLock(long stamp) { - long a = stamp & ABITS, m, s, next; WNode h; + long a, s, next; WNode h; while (((s = state) & SBITS) == (stamp & SBITS)) { - if ((m = s & ABITS) == 0L) { - if (a != 0L) + if ((a = stamp & ABITS) >= WBIT) { + // write stamp + if (s != stamp) break; - else if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) + STATE.setVolatile(this, next = unlockWriteState(s) + RUNIT); + if ((h = whead) != null && h.status != 0) + release(h); + return next; + } + else if (a == 0L) { + // optimistic read stamp + if ((s & ABITS) < RFULL) { + if (STATE.compareAndSet(this, s, next = s + RUNIT)) return next; } else if ((next = tryIncReaderOverflow(s)) != 0L) return next; } - else if (m == WBIT) { - if (a != m) + else { + // already a read stamp + if ((s & ABITS) == 0L) break; - U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT)); - if ((h = whead) != null && h.status != 0) - release(h); - return next; + return stamp; } - else if (a != 0L && a < WBIT) - return stamp; - else - break; } return 0L; } @@ -693,29 +715,22 @@ * @return a valid optimistic read stamp, or zero on failure */ public long tryConvertToOptimisticRead(long stamp) { - long a = stamp & ABITS, m, s, next; WNode h; - U.loadFence(); - for (;;) { - if (((s = state) & SBITS) != (stamp & SBITS)) - break; - if ((m = s & ABITS) == 0L) { - if (a != 0L) + long a, m, s, next; WNode h; + VarHandle.acquireFence(); + while (((s = state) & SBITS) == (stamp & SBITS)) { + if ((a = stamp & ABITS) >= WBIT) { + // write stamp + if (s != stamp) break; - return s; + return unlockWriteInternal(s); } - else if (m == WBIT) { - if (a != m) - break; - U.putLongVolatile(this, STATE, - next = (s += WBIT) == 0L ? ORIGIN : s); - if ((h = whead) != null && h.status != 0) - release(h); - return next; - } - else if (a == 0L || a >= WBIT) + else if (a == 0L) + // already an optimistic read stamp + return stamp; + else if ((m = s & ABITS) == 0L) // invalid read stamp break; else if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, next = s - RUNIT)) { + if (STATE.compareAndSet(this, s, next = s - RUNIT)) { if (m == RUNIT && (h = whead) != null && h.status != 0) release(h); return next & SBITS; @@ -734,12 +749,11 @@ * * @return {@code true} if the lock was held, else false */ + @ReservedStackAccess public boolean tryUnlockWrite() { - long s; WNode h; + long s; if (((s = state) & WBIT) != 0L) { - U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s); - if ((h = whead) != null && h.status != 0) - release(h); + unlockWriteInternal(s); return true; } return false; @@ -752,11 +766,12 @@ * * @return {@code true} if the read lock was held, else false */ + @ReservedStackAccess public boolean tryUnlockRead() { long s, m; WNode h; while ((m = (s = state) & ABITS) != 0L && m < WBIT) { if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + if (STATE.compareAndSet(this, s, s - RUNIT)) { if (m == RUNIT && (h = whead) != null && h.status != 0) release(h); return true; @@ -832,32 +847,30 @@ * Returns a plain {@link Lock} view of this StampedLock in which * the {@link Lock#lock} method is mapped to {@link #readLock}, * and similarly for other methods. The returned Lock does not - * support a {@link Condition}; method {@link - * Lock#newCondition()} throws {@code - * UnsupportedOperationException}. + * support a {@link Condition}; method {@link Lock#newCondition()} + * throws {@code UnsupportedOperationException}. * * @return the lock */ public Lock asReadLock() { ReadLockView v; - return ((v = readLockView) != null ? v : - (readLockView = new ReadLockView())); + if ((v = readLockView) != null) return v; + return readLockView = new ReadLockView(); } /** * Returns a plain {@link Lock} view of this StampedLock in which * the {@link Lock#lock} method is mapped to {@link #writeLock}, * and similarly for other methods. The returned Lock does not - * support a {@link Condition}; method {@link - * Lock#newCondition()} throws {@code - * UnsupportedOperationException}. + * support a {@link Condition}; method {@link Lock#newCondition()} + * throws {@code UnsupportedOperationException}. * * @return the lock */ public Lock asWriteLock() { WriteLockView v; - return ((v = writeLockView) != null ? v : - (writeLockView = new WriteLockView())); + if ((v = writeLockView) != null) return v; + return writeLockView = new WriteLockView(); } /** @@ -870,8 +883,8 @@ */ public ReadWriteLock asReadWriteLock() { ReadWriteLockView v; - return ((v = readWriteLockView) != null ? v : - (readWriteLockView = new ReadWriteLockView())); + if ((v = readWriteLockView) != null) return v; + return readWriteLockView = new ReadWriteLockView(); } // view classes @@ -917,35 +930,32 @@ // Needed because view-class lock methods throw away stamps. final void unstampedUnlockWrite() { - WNode h; long s; + long s; if (((s = state) & WBIT) == 0L) throw new IllegalMonitorStateException(); - U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s); - if ((h = whead) != null && h.status != 0) - release(h); + unlockWriteInternal(s); } final void unstampedUnlockRead() { - for (;;) { - long s, m; WNode h; - if ((m = (s = state) & ABITS) == 0L || m >= WBIT) - throw new IllegalMonitorStateException(); - else if (m < RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) { + long s, m; WNode h; + while ((m = (s = state) & RBITS) > 0L) { + if (m < RFULL) { + if (STATE.compareAndSet(this, s, s - RUNIT)) { if (m == RUNIT && (h = whead) != null && h.status != 0) release(h); - break; + return; } } else if (tryDecReaderOverflow(s) != 0L) - break; + return; } + throw new IllegalMonitorStateException(); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state + STATE.setVolatile(this, ORIGIN); // reset to unlocked state } // internals @@ -961,15 +971,16 @@ private long tryIncReaderOverflow(long s) { // assert (s & ABITS) >= RFULL; if ((s & ABITS) == RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) { + if (STATE.compareAndSet(this, s, s | RBITS)) { ++readerOverflow; - U.putLongVolatile(this, STATE, s); + STATE.setVolatile(this, s); return s; } } - else if ((LockSupport.nextSecondarySeed() & - OVERFLOW_YIELD_RATE) == 0) + else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0) Thread.yield(); + else + Thread.onSpinWait(); return 0L; } @@ -982,7 +993,7 @@ private long tryDecReaderOverflow(long s) { // assert (s & ABITS) >= RFULL; if ((s & ABITS) == RFULL) { - if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) { + if (STATE.compareAndSet(this, s, s | RBITS)) { int r; long next; if ((r = readerOverflow) > 0) { readerOverflow = r - 1; @@ -990,13 +1001,14 @@ } else next = s - RUNIT; - U.putLongVolatile(this, STATE, next); + STATE.setVolatile(this, next); return next; } } - else if ((LockSupport.nextSecondarySeed() & - OVERFLOW_YIELD_RATE) == 0) + else if ((LockSupport.nextSecondarySeed() & OVERFLOW_YIELD_RATE) == 0) Thread.yield(); + else + Thread.onSpinWait(); return 0L; } @@ -1010,14 +1022,14 @@ private void release(WNode h) { if (h != null) { WNode q; Thread w; - U.compareAndSwapInt(h, WSTATUS, WAITING, 0); + WSTATUS.compareAndSet(h, WAITING, 0); if ((q = h.next) == null || q.status == CANCELLED) { for (WNode t = wtail; t != null && t != h; t = t.prev) if (t.status <= 0) q = t; } if (q != null && (w = q.thread) != null) - U.unpark(w); + LockSupport.unpark(w); } } @@ -1035,25 +1047,25 @@ for (int spins = -1;;) { // spin while enqueuing long m, s, ns; if ((m = (s = state) & ABITS) == 0L) { - if (U.compareAndSwapLong(this, STATE, s, ns = s + WBIT)) + if (STATE.compareAndSet(this, s, ns = s + WBIT)) return ns; } else if (spins < 0) spins = (m == WBIT && wtail == whead) ? SPINS : 0; else if (spins > 0) { - if (LockSupport.nextSecondarySeed() >= 0) - --spins; + --spins; + Thread.onSpinWait(); } else if ((p = wtail) == null) { // initialize queue WNode hd = new WNode(WMODE, null); - if (U.compareAndSwapObject(this, WHEAD, null, hd)) + if (WHEAD.weakCompareAndSetVolatile(this, null, hd)) wtail = hd; } else if (node == null) node = new WNode(WMODE, p); else if (node.prev != p) node.prev = p; - else if (U.compareAndSwapObject(this, WTAIL, p, node)) { + else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) { p.next = node; break; } @@ -1067,11 +1079,10 @@ spins = HEAD_SPINS; else if (spins < MAX_HEAD_SPINS) spins <<= 1; - for (int k = spins;;) { // spin at head + for (int k = spins; k > 0; --k) { // spin at head long s, ns; if (((s = state) & ABITS) == 0L) { - if (U.compareAndSwapLong(this, STATE, s, - ns = s + WBIT)) { + if (STATE.compareAndSet(this, s, ns = s + WBIT)) { whead = node; node.prev = null; if (wasInterrupted) @@ -1079,17 +1090,16 @@ return ns; } } - else if (LockSupport.nextSecondarySeed() >= 0 && - --k <= 0) - break; + else + Thread.onSpinWait(); } } else if (h != null) { // help release stale waiters WNode c; Thread w; while ((c = h.cowait) != null) { - if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && + if (WCOWAIT.weakCompareAndSetVolatile(h, c, c.cowait) && (w = c.thread) != null) - U.unpark(w); + LockSupport.unpark(w); } } if (whead == h) { @@ -1098,7 +1108,7 @@ (p = np).next = node; // stale } else if ((ps = p.status) == 0) - U.compareAndSwapInt(p, WSTATUS, 0, WAITING); + WSTATUS.compareAndSet(p, 0, WAITING); else if (ps == CANCELLED) { if ((pp = p.prev) != null) { node.prev = pp; @@ -1112,13 +1122,15 @@ else if ((time = deadline - System.nanoTime()) <= 0L) return cancelWaiter(node, node, false); Thread wt = Thread.currentThread(); - U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if (p.status < 0 && (p != h || (state & ABITS) != 0L) && - whead == h && node.prev == p) - U.park(false, time); // emulate LockSupport.park + whead == h && node.prev == p) { + if (time == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, time); + } node.thread = null; - U.putObject(wt, PARKBLOCKER, null); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, node, true); @@ -1146,7 +1158,7 @@ if ((h = whead) == (p = wtail)) { for (long m, s, ns;;) { if ((m = (s = state) & ABITS) < RFULL ? - U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) : + STATE.compareAndSet(this, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { if (wasInterrupted) Thread.currentThread().interrupt(); @@ -1154,8 +1166,8 @@ } else if (m >= WBIT) { if (spins > 0) { - if (LockSupport.nextSecondarySeed() >= 0) - --spins; + --spins; + Thread.onSpinWait(); } else { if (spins == 0) { @@ -1170,7 +1182,7 @@ } if (p == null) { // initialize queue WNode hd = new WNode(WMODE, null); - if (U.compareAndSwapObject(this, WHEAD, null, hd)) + if (WHEAD.weakCompareAndSetVolatile(this, null, hd)) wtail = hd; } else if (node == null) @@ -1178,27 +1190,25 @@ else if (h == p || p.mode != RMODE) { if (node.prev != p) node.prev = p; - else if (U.compareAndSwapObject(this, WTAIL, p, node)) { + else if (WTAIL.weakCompareAndSetVolatile(this, p, node)) { p.next = node; break; } } - else if (!U.compareAndSwapObject(p, WCOWAIT, - node.cowait = p.cowait, node)) + else if (!WCOWAIT.compareAndSet(p, node.cowait = p.cowait, node)) node.cowait = null; else { for (;;) { WNode pp, c; Thread w; if ((h = whead) != null && (c = h.cowait) != null && - U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && + WCOWAIT.compareAndSet(h, c, c.cowait) && (w = c.thread) != null) // help release - U.unpark(w); + LockSupport.unpark(w); if (h == (pp = p.prev) || h == p || pp == null) { long m, s, ns; do { if ((m = (s = state) & ABITS) < RFULL ? - U.compareAndSwapLong(this, STATE, s, - ns = s + RUNIT) : + STATE.compareAndSet(this, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { if (wasInterrupted) @@ -1221,13 +1231,15 @@ return cancelWaiter(node, p, false); } Thread wt = Thread.currentThread(); - U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if ((h != pp || (state & ABITS) == WBIT) && - whead == h && p.prev == pp) - U.park(false, time); + whead == h && p.prev == pp) { + if (time == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, time); + } node.thread = null; - U.putObject(wt, PARKBLOCKER, null); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, p, true); @@ -1248,32 +1260,32 @@ for (int k = spins;;) { // spin at head long m, s, ns; if ((m = (s = state) & ABITS) < RFULL ? - U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) : + STATE.compareAndSet(this, s, ns = s + RUNIT) : (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) { WNode c; Thread w; whead = node; node.prev = null; while ((c = node.cowait) != null) { - if (U.compareAndSwapObject(node, WCOWAIT, - c, c.cowait) && + if (WCOWAIT.compareAndSet(node, c, c.cowait) && (w = c.thread) != null) - U.unpark(w); + LockSupport.unpark(w); } if (wasInterrupted) Thread.currentThread().interrupt(); return ns; } - else if (m >= WBIT && - LockSupport.nextSecondarySeed() >= 0 && --k <= 0) + else if (m >= WBIT && --k <= 0) break; + else + Thread.onSpinWait(); } } else if (h != null) { WNode c; Thread w; while ((c = h.cowait) != null) { - if (U.compareAndSwapObject(h, WCOWAIT, c, c.cowait) && + if (WCOWAIT.compareAndSet(h, c, c.cowait) && (w = c.thread) != null) - U.unpark(w); + LockSupport.unpark(w); } } if (whead == h) { @@ -1282,7 +1294,7 @@ (p = np).next = node; // stale } else if ((ps = p.status) == 0) - U.compareAndSwapInt(p, WSTATUS, 0, WAITING); + WSTATUS.compareAndSet(p, 0, WAITING); else if (ps == CANCELLED) { if ((pp = p.prev) != null) { node.prev = pp; @@ -1296,14 +1308,16 @@ else if ((time = deadline - System.nanoTime()) <= 0L) return cancelWaiter(node, node, false); Thread wt = Thread.currentThread(); - U.putObject(wt, PARKBLOCKER, this); node.thread = wt; if (p.status < 0 && (p != h || (state & ABITS) == WBIT) && - whead == h && node.prev == p) - U.park(false, time); + whead == h && node.prev == p) { + if (time == 0L) + LockSupport.park(this); + else + LockSupport.parkNanos(this, time); + } node.thread = null; - U.putObject(wt, PARKBLOCKER, null); if (Thread.interrupted()) { if (interruptible) return cancelWaiter(node, node, true); @@ -1325,7 +1339,7 @@ * AbstractQueuedSynchronizer (see its detailed explanation in AQS * internal documentation). * - * @param node if nonnull, the waiter + * @param node if non-null, the waiter * @param group either node or the group node is cowaiting with * @param interrupted if already interrupted * @return INTERRUPTED if interrupted or Thread.interrupted, else zero @@ -1337,7 +1351,7 @@ // unsplice cancelled nodes from group for (WNode p = group, q; (q = p.cowait) != null;) { if (q.status == CANCELLED) { - U.compareAndSwapObject(p, WCOWAIT, q, q.cowait); + WCOWAIT.compareAndSet(p, q, q.cowait); p = group; // restart } else @@ -1346,7 +1360,7 @@ if (group == node) { for (WNode r = group.cowait; r != null; r = r.cowait) { if ((w = r.thread) != null) - U.unpark(w); // wake up uncancelled co-waiters + LockSupport.unpark(w); // wake up uncancelled co-waiters } for (WNode pred = node.prev; pred != null; ) { // unsplice WNode succ, pp; // find valid successor @@ -1357,23 +1371,23 @@ if (t.status != CANCELLED) q = t; // don't link if succ cancelled if (succ == q || // ensure accurate successor - U.compareAndSwapObject(node, WNEXT, - succ, succ = q)) { + WNEXT.compareAndSet(node, succ, succ = q)) { if (succ == null && node == wtail) - U.compareAndSwapObject(this, WTAIL, node, pred); + WTAIL.compareAndSet(this, node, pred); break; } } if (pred.next == node) // unsplice pred link - U.compareAndSwapObject(pred, WNEXT, node, succ); + WNEXT.compareAndSet(pred, node, succ); if (succ != null && (w = succ.thread) != null) { + // wake up succ to observe new pred succ.thread = null; - U.unpark(w); // wake up succ to observe new pred + LockSupport.unpark(w); } if (pred.status != CANCELLED || (pp = pred.prev) == null) break; node.prev = pp; // repeat if new pred wrong/cancelled - U.compareAndSwapObject(pp, WNEXT, pred, succ); + WNEXT.compareAndSet(pp, pred, succ); pred = pp; } } @@ -1397,34 +1411,22 @@ return (interrupted || Thread.interrupted()) ? INTERRUPTED : 0L; } - // Unsafe mechanics - private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe(); - private static final long STATE; - private static final long WHEAD; - private static final long WTAIL; - private static final long WNEXT; - private static final long WSTATUS; - private static final long WCOWAIT; - private static final long PARKBLOCKER; - + // VarHandle mechanics + private static final VarHandle STATE; + private static final VarHandle WHEAD; + private static final VarHandle WTAIL; + private static final VarHandle WNEXT; + private static final VarHandle WSTATUS; + private static final VarHandle WCOWAIT; static { try { - STATE = U.objectFieldOffset - (StampedLock.class.getDeclaredField("state")); - WHEAD = U.objectFieldOffset - (StampedLock.class.getDeclaredField("whead")); - WTAIL = U.objectFieldOffset - (StampedLock.class.getDeclaredField("wtail")); - - WSTATUS = U.objectFieldOffset - (WNode.class.getDeclaredField("status")); - WNEXT = U.objectFieldOffset - (WNode.class.getDeclaredField("next")); - WCOWAIT = U.objectFieldOffset - (WNode.class.getDeclaredField("cowait")); - - PARKBLOCKER = U.objectFieldOffset - (Thread.class.getDeclaredField("parkBlocker")); + MethodHandles.Lookup l = MethodHandles.lookup(); + STATE = l.findVarHandle(StampedLock.class, "state", long.class); + WHEAD = l.findVarHandle(StampedLock.class, "whead", WNode.class); + WTAIL = l.findVarHandle(StampedLock.class, "wtail", WNode.class); + WSTATUS = l.findVarHandle(WNode.class, "status", int.class); + WNEXT = l.findVarHandle(WNode.class, "next", WNode.class); + WCOWAIT = l.findVarHandle(WNode.class, "cowait", WNode.class); } catch (ReflectiveOperationException e) { throw new Error(e); } diff -r 5bf88dce615f -r a0b0fa5763b1 jdk/test/java/util/concurrent/tck/StampedLockTest.java --- a/jdk/test/java/util/concurrent/tck/StampedLockTest.java Wed Jul 05 21:58:29 2017 +0200 +++ b/jdk/test/java/util/concurrent/tck/StampedLockTest.java Fri Jul 15 13:51:43 2016 -0700 @@ -745,28 +745,41 @@ public void testTryConvertToOptimisticRead() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertEquals(0L, lock.tryConvertToOptimisticRead(0L)); + assertTrue((s = lock.tryOptimisticRead()) != 0L); - assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); + assertEquals(s, lock.tryConvertToOptimisticRead(s)); + assertTrue(lock.validate(s)); + + assertTrue((p = lock.readLock()) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); + assertEquals(s, lock.tryConvertToOptimisticRead(s)); + assertTrue(lock.validate(s)); + lock.unlockRead(p); + assertTrue((s = lock.writeLock()) != 0L); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToOptimisticRead(s)) != 0L); @@ -780,39 +793,67 @@ public void testTryConvertToReadLock() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToReadLock(s)) != 0L); + + assertFalse((p = lock.tryConvertToReadLock(0L)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + + assertTrue((s = lock.tryOptimisticRead()) != 0L); + lock.readLock(); + assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); + assertTrue(lock.isReadLocked()); + assertEquals(2, lock.getReadLockCount()); + lock.unlockRead(p); + lock.unlockRead(p); + assertTrue((s = lock.writeLock()) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); lock.unlockRead(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToReadLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockRead(p); + assertEquals(s, lock.tryConvertToReadLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isReadLocked()); + assertEquals(1, lock.getReadLockCount()); + lock.unlockRead(s); } /** @@ -822,38 +863,52 @@ public void testTryConvertToWriteLock() throws InterruptedException { StampedLock lock = new StampedLock(); long s, p; - s = 0L; - assertFalse((p = lock.tryConvertToWriteLock(s)) != 0L); + + assertFalse((p = lock.tryConvertToWriteLock(0L)) != 0L); + assertTrue((s = lock.tryOptimisticRead()) != 0L); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.writeLock()) != 0L); - assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockWrite(p); + assertEquals(s, lock.tryConvertToWriteLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + assertTrue((s = lock.readLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock()) != 0L); assertTrue(lock.validate(s)); - assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); - assertTrue(lock.validate(p)); - lock.unlockWrite(p); + assertEquals(s, lock.tryConvertToWriteLock(s)); + assertTrue(lock.validate(s)); + assertTrue(lock.isWriteLocked()); + lock.unlockWrite(s); + assertTrue((s = lock.tryReadLock()) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryWriteLock(100L, MILLISECONDS)) != 0L); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); + assertTrue((s = lock.tryReadLock(100L, MILLISECONDS)) != 0L); assertTrue(lock.validate(s)); assertTrue((p = lock.tryConvertToWriteLock(s)) != 0L); assertTrue(lock.validate(p)); + assertTrue(lock.isWriteLocked()); lock.unlockWrite(p); } @@ -903,4 +958,124 @@ assertTrue(lock.tryLock()); } + /** + * Lock.newCondition throws UnsupportedOperationException + */ + public void testLockViewsDoNotSupportConditions() { + StampedLock sl = new StampedLock(); + assertThrows(UnsupportedOperationException.class, + () -> sl.asWriteLock().newCondition(), + () -> sl.asReadLock().newCondition(), + () -> sl.asReadWriteLock().writeLock().newCondition(), + () -> sl.asReadWriteLock().readLock().newCondition()); + } + + /** + * Passing optimistic read stamps to unlock operations result in + * IllegalMonitorStateException + */ + public void testCannotUnlockOptimisticReadStamps() { + Runnable[] actions = { + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + assertTrue(stamp != 0); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryOptimisticRead(); + sl.readLock(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + assertTrue(stamp != 0); + sl.writeLock(); + sl.unlockWrite(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.writeLock()); + sl.readLock(); + sl.unlock(stamp); + }, + + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertTrue(stamp != 0); + sl.writeLock(); + sl.unlockWrite(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.writeLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + assertTrue(stamp != 0); + sl.readLock(); + sl.unlockRead(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlock(stamp); + }, + () -> { + StampedLock sl = new StampedLock(); + sl.readLock(); + long stamp = sl.tryConvertToOptimisticRead(sl.readLock()); + sl.readLock(); + sl.unlock(stamp); + }, + }; + + assertThrows(IllegalMonitorStateException.class, actions); + } + }