From 0f49a089d68e922845b4d7aaba8d837f8b4dc6d2 Mon Sep 17 00:00:00 2001 From: Doug Lea
The wait queue is a variant of a "CLH" (Craig, Landin, and - * Hagersten) lock queue. CLH locks are normally used for - * spinlocks. We instead use them for blocking synchronizers, but - * use the same basic tactic of holding some of the control - * information about a thread in the predecessor of its node. A - * "status" field in each node keeps track of whether a thread - * should block. A node is signalled when its predecessor - * releases. Each node of the queue otherwise serves as a - * specific-notification-style monitor holding a single waiting - * thread. The status field does NOT control whether threads are - * granted locks etc though. A thread may try to acquire if it is - * first in the queue. But being first does not guarantee success; - * it only gives the right to contend. So the currently released - * contender thread may need to rewait. - * - *
To enqueue into a CLH lock, you atomically splice it in as new - * tail. To dequeue, you just set the head field. - *
- * +------+ prev +-----+ +-----+ - * head | | <---- | | <---- | | tail - * +------+ +-----+ +-----+ - *- * - *
Insertion into a CLH queue requires only a single atomic - * operation on "tail", so there is a simple atomic point of - * demarcation from unqueued to queued. Similarly, dequeuing - * involves only updating the "head". However, it takes a bit - * more work for nodes to determine who their successors are, - * in part to deal with possible cancellation due to timeouts - * and interrupts. - * - *
The "prev" links (not used in original CLH locks), are mainly - * needed to handle cancellation. If a node is cancelled, its - * successor is (normally) relinked to a non-cancelled - * predecessor. For explanation of similar mechanics in the case - * of spin locks, see the papers by Scott and Scherer at - * http://www.cs.rochester.edu/u/scott/synchronization/ - * - *
We also use "next" links to implement blocking mechanics. - * The thread id for each node is kept in its own node, so a - * predecessor signals the next node to wake up by traversing - * next link to determine which thread it is. Determination of - * successor must avoid races with newly queued nodes to set - * the "next" fields of their predecessors. This is solved - * when necessary by checking backwards from the atomically - * updated "tail" when a node's successor appears to be null. - * (Or, said differently, the next-links are an optimization - * so that we don't usually need a backward scan.) - * - *
Cancellation introduces some conservatism to the basic - * algorithms. Since we must poll for cancellation of other - * nodes, we can miss noticing whether a cancelled node is - * ahead or behind us. This is dealt with by always unparking - * successors upon cancellation, allowing them to stabilize on - * a new predecessor, unless we can identify an uncancelled - * predecessor who will carry this responsibility. - * - *
CLH queues need a dummy header node to get started. But - * we don't create them on construction, because it would be wasted - * effort if there is never contention. Instead, the node - * is constructed and head and tail pointers are set upon first - * contention. - * - *
Threads waiting on Conditions use the same nodes, but - * use an additional link. Conditions only need to link nodes - * in simple (non-concurrent) linked queues because they are - * only accessed when exclusively held. Upon await, a node is - * inserted into a condition queue. Upon signal, the node is - * transferred to the main queue. A special value of status - * field is used to mark which queue a node is on. - * - *
Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill - * Scherer and Michael Scott, along with members of JSR-166 - * expert group, for helpful ideas, discussions, and critiques - * on the design of this class. - */ - static final class Node { - /** Marker to indicate a node is waiting in shared mode */ - static final Node SHARED = new Node(); - /** Marker to indicate a node is waiting in exclusive mode */ - static final Node EXCLUSIVE = null; - - /** waitStatus value to indicate thread has cancelled */ - static final int CANCELLED = 1; - /** waitStatus value to indicate successor's thread needs unparking */ - static final int SIGNAL = -1; - /** waitStatus value to indicate thread is waiting on condition */ - static final int CONDITION = -2; - /** - * waitStatus value to indicate the next acquireShared should - * unconditionally propagate - */ - static final int PROPAGATE = -3; - - /** - * Status field, taking on only the values: - * SIGNAL: The successor of this node is (or will soon be) - * blocked (via park), so the current node must - * unpark its successor when it releases or - * cancels. To avoid races, acquire methods must - * first indicate they need a signal, - * then retry the atomic acquire, and then, - * on failure, block. - * CANCELLED: This node is cancelled due to timeout or interrupt. - * Nodes never leave this state. In particular, - * a thread with cancelled node never again blocks. - * CONDITION: This node is currently on a condition queue. - * It will not be used as a sync queue node - * until transferred, at which time the status - * will be set to 0. (Use of this value here has - * nothing to do with the other uses of the - * field, but simplifies mechanics.) - * PROPAGATE: A releaseShared should be propagated to other - * nodes. This is set (for head node only) in - * doReleaseShared to ensure propagation - * continues, even if other operations have - * since intervened. - * 0: None of the above - * - * The values are arranged numerically to simplify use. - * Non-negative values mean that a node doesn't need to - * signal. So, most code doesn't need to check for particular - * values, just for sign. - * - * The field is initialized to 0 for normal sync nodes, and - * CONDITION for condition nodes. It is modified using CAS - * (or when possible, unconditional volatile writes). - */ - volatile int waitStatus; - - /** - * Link to predecessor node that current node/thread relies on - * for checking waitStatus. Assigned during enqueuing, and nulled - * out (for sake of GC) only upon dequeuing. Also, upon - * cancellation of a predecessor, we short-circuit while - * finding a non-cancelled one, which will always exist - * because the head node is never cancelled: A node becomes - * head only as a result of successful acquire. A - * cancelled thread never succeeds in acquiring, and a thread only - * cancels itself, not any other node. - */ - volatile Node prev; - - /** - * Link to the successor node that the current node/thread - * unparks upon release. Assigned during enqueuing, adjusted - * when bypassing cancelled predecessors, and nulled out (for - * sake of GC) when dequeued. The enq operation does not - * assign next field of a predecessor until after attachment, - * so seeing a null next field does not necessarily mean that - * node is at end of queue. However, if a next field appears - * to be null, we can scan prev's from the tail to - * double-check. The next field of cancelled nodes is set to - * point to the node itself instead of null, to make life - * easier for isOnSyncQueue. - */ - volatile Node next; - - /** - * The thread that enqueued this node. Initialized on - * construction and nulled out after use. - */ - volatile Thread thread; - - /** - * Link to next node waiting on condition, or the special - * value SHARED. Because condition queues are accessed only - * when holding in exclusive mode, we just need a simple - * linked queue to hold nodes while they are waiting on - * conditions. They are then transferred to the queue to - * re-acquire. And because conditions can only be exclusive, - * we save a field by using special value to indicate shared - * mode. - */ - Node nextWaiter; - - /** - * Returns true if node is waiting in shared mode. - */ - final boolean isShared() { - return nextWaiter == SHARED; - } - - /** - * Returns previous node, or throws NullPointerException if null. - * Use when predecessor cannot be null. The null check could - * be elided, but is present to help the VM. - * - * @return the predecessor of this node - */ - final Node predecessor() throws NullPointerException { - Node p = prev; - if (p == null) - throw new NullPointerException(); - else - return p; - } - - Node() { // Used to establish initial head or SHARED marker - } - - Node(Thread thread, Node mode) { // Used by addWaiter - this.nextWaiter = mode; - this.thread = thread; - } - - Node(Thread thread, int waitStatus) { // Used by Condition - this.waitStatus = waitStatus; - this.thread = thread; - } - } - /** * Head of the wait queue, lazily initialized. Except for * initialization, it is modified only via method setHead. Note: @@ -325,7 +111,9 @@ public abstract class AbstractQueuedLongSynchronizer * @param newState the new state value */ protected final void setState(long newState) { - state = newState; + // Use putLongVolatile instead of ordinary volatile store when + // using compareAndSwapLong, for sake of some 32bit systems. + U.putLongVolatile(this, STATE, newState); } /** @@ -340,8 +128,7 @@ public abstract class AbstractQueuedLongSynchronizer * value was not equal to the expected value. */ protected final boolean compareAndSetState(long expect, long update) { - // See below for intrinsics setup to support this - return unsafe.compareAndSwapLong(this, stateOffset, expect, update); + return U.compareAndSwapLong(this, STATE, expect, update); } // Queuing utilities @@ -351,25 +138,24 @@ public abstract class AbstractQueuedLongSynchronizer * rather than to use timed park. A rough estimate suffices * to improve responsiveness with very short timeouts. */ - static final long spinForTimeoutThreshold = 1000L; + static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L; /** * Inserts node into queue, initializing if necessary. See picture above. * @param node the node to insert * @return node's predecessor */ - private Node enq(final Node node) { + private Node enq(Node node) { for (;;) { - Node t = tail; - if (t == null) { // Must initialize - if (compareAndSetHead(new Node())) - tail = head; - } else { - node.prev = t; - if (compareAndSetTail(t, node)) { - t.next = node; - return t; + Node oldTail = tail; + if (oldTail != null) { + U.putObject(node, Node.PREV, oldTail); + if (compareAndSetTail(oldTail, node)) { + oldTail.next = node; + return oldTail; } + } else { + initializeSyncQueue(); } } } @@ -381,18 +167,20 @@ public abstract class AbstractQueuedLongSynchronizer * @return the new node */ private Node addWaiter(Node mode) { - Node node = new Node(Thread.currentThread(), mode); - // Try the fast path of enq; backup to full enq on failure - Node pred = tail; - if (pred != null) { - node.prev = pred; - if (compareAndSetTail(pred, node)) { - pred.next = node; - return node; + Node node = new Node(mode); + + for (;;) { + Node oldTail = tail; + if (oldTail != null) { + U.putObject(node, Node.PREV, oldTail); + if (compareAndSetTail(oldTail, node)) { + oldTail.next = node; + return node; + } + } else { + initializeSyncQueue(); } } - enq(node); - return node; } /** @@ -421,7 +209,7 @@ public abstract class AbstractQueuedLongSynchronizer */ int ws = node.waitStatus; if (ws < 0) - compareAndSetWaitStatus(node, ws, 0); + node.compareAndSetWaitStatus(ws, 0); /* * Thread to unpark is held in successor, which is normally @@ -432,9 +220,9 @@ public abstract class AbstractQueuedLongSynchronizer Node s = node.next; if (s == null || s.waitStatus > 0) { s = null; - for (Node t = tail; t != null && t != node; t = t.prev) - if (t.waitStatus <= 0) - s = t; + for (Node p = tail; p != node && p != null; p = p.prev) + if (p.waitStatus <= 0) + s = p; } if (s != null) LockSupport.unpark(s.thread); @@ -462,12 +250,12 @@ public abstract class AbstractQueuedLongSynchronizer if (h != null && h != tail) { int ws = h.waitStatus; if (ws == Node.SIGNAL) { - if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) + if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0)) continue; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && - !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) + !h.compareAndSetWaitStatus(0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed @@ -541,18 +329,18 @@ public abstract class AbstractQueuedLongSynchronizer // If we are the tail, remove ourselves. if (node == tail && compareAndSetTail(node, pred)) { - compareAndSetNext(pred, predNext, null); + pred.compareAndSetNext(predNext, null); } else { // If successor needs signal, try to set pred's next-link // so it will get one. Otherwise wake it up to propagate. int ws; if (pred != head && ((ws = pred.waitStatus) == Node.SIGNAL || - (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) && + (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) && pred.thread != null) { Node next = node.next; if (next != null && next.waitStatus <= 0) - compareAndSetNext(pred, predNext, next); + pred.compareAndSetNext(predNext, next); } else { unparkSuccessor(node); } @@ -593,7 +381,7 @@ public abstract class AbstractQueuedLongSynchronizer * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ - compareAndSetWaitStatus(pred, ws, Node.SIGNAL); + pred.compareAndSetWaitStatus(ws, Node.SIGNAL); } return false; } @@ -606,7 +394,7 @@ public abstract class AbstractQueuedLongSynchronizer } /** - * Convenience method to park and then check if interrupted + * Convenience method to park and then check if interrupted. * * @return {@code true} if interrupted */ @@ -633,7 +421,6 @@ public abstract class AbstractQueuedLongSynchronizer * @return {@code true} if interrupted while waiting */ final boolean acquireQueued(final Node node, long arg) { - boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -641,16 +428,15 @@ public abstract class AbstractQueuedLongSynchronizer if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return interrupted; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -661,23 +447,21 @@ public abstract class AbstractQueuedLongSynchronizer private void doAcquireInterruptibly(long arg) throws InterruptedException { final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return; } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -694,28 +478,28 @@ public abstract class AbstractQueuedLongSynchronizer return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.EXCLUSIVE); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC - failed = false; return true; } nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) + if (nanosTimeout <= 0L) { + cancelAcquire(node); return false; + } if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -725,7 +509,6 @@ public abstract class AbstractQueuedLongSynchronizer */ private void doAcquireShared(long arg) { final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { boolean interrupted = false; for (;;) { @@ -737,7 +520,6 @@ public abstract class AbstractQueuedLongSynchronizer p.next = null; // help GC if (interrupted) selfInterrupt(); - failed = false; return; } } @@ -745,9 +527,9 @@ public abstract class AbstractQueuedLongSynchronizer parkAndCheckInterrupt()) interrupted = true; } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -758,7 +540,6 @@ public abstract class AbstractQueuedLongSynchronizer private void doAcquireSharedInterruptibly(long arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -767,7 +548,6 @@ public abstract class AbstractQueuedLongSynchronizer if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC - failed = false; return; } } @@ -775,9 +555,9 @@ public abstract class AbstractQueuedLongSynchronizer parkAndCheckInterrupt()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -794,7 +574,6 @@ public abstract class AbstractQueuedLongSynchronizer return false; final long deadline = System.nanoTime() + nanosTimeout; final Node node = addWaiter(Node.SHARED); - boolean failed = true; try { for (;;) { final Node p = node.predecessor(); @@ -803,22 +582,23 @@ public abstract class AbstractQueuedLongSynchronizer if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC - failed = false; return true; } } nanosTimeout = deadline - System.nanoTime(); - if (nanosTimeout <= 0L) + if (nanosTimeout <= 0L) { + cancelAcquire(node); return false; + } if (shouldParkAfterFailedAcquire(p, node) && - nanosTimeout > spinForTimeoutThreshold) + nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD) LockSupport.parkNanos(this, nanosTimeout); if (Thread.interrupted()) throw new InterruptedException(); } - } finally { - if (failed) - cancelAcquire(node); + } catch (Throwable t) { + cancelAcquire(node); + throw t; } } @@ -1170,7 +950,7 @@ public abstract class AbstractQueuedLongSynchronizer } /** - * Version of getFirstQueuedThread called when fastpath fails + * Version of getFirstQueuedThread called when fastpath fails. */ private Thread fullGetFirstQueuedThread() { /* @@ -1250,7 +1030,7 @@ public abstract class AbstractQueuedLongSynchronizer * *
An invocation of this method is equivalent to (but may be * more efficient than): - *
{@code
+ * {@code
* getFirstQueuedThread() != Thread.currentThread() &&
* hasQueuedThreads()}
*
@@ -1270,7 +1050,7 @@ public abstract class AbstractQueuedLongSynchronizer
* tryAcquire} method for a fair, reentrant, exclusive mode
* synchronizer might look like this:
*
- * {@code
+ * {@code
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
@@ -1306,8 +1086,7 @@ public abstract class AbstractQueuedLongSynchronizer
* acquire. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting to acquire
*/
@@ -1332,7 +1111,7 @@ public abstract class AbstractQueuedLongSynchronizer
* @return the collection of threads
*/
public final Collection getQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
@@ -1350,7 +1129,7 @@ public abstract class AbstractQueuedLongSynchronizer
* @return the collection of threads
*/
public final Collection getExclusiveQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) {
Thread t = p.thread;
@@ -1370,7 +1149,7 @@ public abstract class AbstractQueuedLongSynchronizer
* @return the collection of threads
*/
public final Collection getSharedQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) {
Thread t = p.thread;
@@ -1391,10 +1170,9 @@ public abstract class AbstractQueuedLongSynchronizer
* @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
- long s = getState();
- String q = hasQueuedThreads() ? "non" : "";
- return super.toString() +
- "[State = " + s + ", " + q + "empty queue]";
+ return super.toString()
+ + "[State = " + getState() + ", "
+ + (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
@@ -1428,13 +1206,15 @@ public abstract class AbstractQueuedLongSynchronizer
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
- Node t = tail;
- for (;;) {
- if (t == node)
+ // We check for node first, since it's likely to be at or near tail.
+ // tail is known to be non-null, so we could re-order to "save"
+ // one null check, but we leave it this way to help the VM.
+ for (Node p = tail;;) {
+ if (p == node)
return true;
- if (t == null)
+ if (p == null)
return false;
- t = t.prev;
+ p = p.prev;
}
}
@@ -1449,7 +1229,7 @@ public abstract class AbstractQueuedLongSynchronizer
/*
* If cannot change waitStatus, the node has been cancelled.
*/
- if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
+ if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
@@ -1460,7 +1240,7 @@ public abstract class AbstractQueuedLongSynchronizer
*/
Node p = enq(node);
int ws = p.waitStatus;
- if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
+ if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
@@ -1473,7 +1253,7 @@ public abstract class AbstractQueuedLongSynchronizer
* @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
- if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
+ if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
enq(node);
return true;
}
@@ -1495,18 +1275,14 @@ public abstract class AbstractQueuedLongSynchronizer
* @return previous sync state
*/
final long fullyRelease(Node node) {
- boolean failed = true;
try {
long savedState = getState();
- if (release(savedState)) {
- failed = false;
+ if (release(savedState))
return savedState;
- } else {
- throw new IllegalMonitorStateException();
- }
- } finally {
- if (failed)
- node.waitStatus = Node.CANCELLED;
+ throw new IllegalMonitorStateException();
+ } catch (Throwable t) {
+ node.waitStatus = Node.CANCELLED;
+ throw t;
}
}
@@ -1551,8 +1327,8 @@ public abstract class AbstractQueuedLongSynchronizer
* given condition associated with this synchronizer. Note that
* because timeouts and interrupts may occur at any time, the
* estimate serves only as an upper bound on the actual number of
- * waiters. This method is designed for use in monitoring of the
- * system state, not for synchronization control.
+ * waiters. This method is designed for use in monitoring system
+ * state, not for synchronization control.
*
* @param condition the condition
* @return the estimated number of waiting threads
@@ -1632,7 +1408,9 @@ public abstract class AbstractQueuedLongSynchronizer
unlinkCancelledWaiters();
t = lastWaiter;
}
- Node node = new Node(Thread.currentThread(), Node.CONDITION);
+
+ Node node = new Node(Node.CONDITION);
+
if (t == null)
firstWaiter = node;
else
@@ -1740,12 +1518,12 @@ public abstract class AbstractQueuedLongSynchronizer
/**
* Implements uninterruptible condition wait.
*
- * - Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
*
*/
public final void awaitUninterruptibly() {
@@ -1799,14 +1577,14 @@ public abstract class AbstractQueuedLongSynchronizer
/**
* Implements interruptible condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled or interrupted.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled or interrupted.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
*
*/
public final void await() throws InterruptedException {
@@ -1831,30 +1609,33 @@ public abstract class AbstractQueuedLongSynchronizer
/**
* Implements timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
*
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // awaitNanos(0) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
+ long initialNanos = nanosTimeout;
Node node = addConditionWaiter();
long savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -1866,21 +1647,22 @@ public abstract class AbstractQueuedLongSynchronizer
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
- return deadline - System.nanoTime();
+ long remaining = deadline - System.nanoTime(); // avoid overflow
+ return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
}
/**
* Implements absolute timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
- *
- If timed out while blocked in step 4, return false, else true.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If timed out while blocked in step 4, return false, else true.
*
*/
public final boolean awaitUntil(Date deadline)
@@ -1893,7 +1675,7 @@ public abstract class AbstractQueuedLongSynchronizer
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() > abstime) {
+ if (System.currentTimeMillis() >= abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
@@ -1913,15 +1695,15 @@ public abstract class AbstractQueuedLongSynchronizer
/**
* Implements timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
- *
- If timed out while blocked in step 4, return false, else true.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If timed out while blocked in step 4, return false, else true.
*
*/
public final boolean await(long time, TimeUnit unit)
@@ -1929,9 +1711,11 @@ public abstract class AbstractQueuedLongSynchronizer
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // await(0, unit) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
Node node = addConditionWaiter();
long savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
@@ -1939,7 +1723,7 @@ public abstract class AbstractQueuedLongSynchronizer
timedout = transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -2016,7 +1800,7 @@ public abstract class AbstractQueuedLongSynchronizer
protected final Collection getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) {
Thread t = w.thread;
@@ -2037,59 +1821,40 @@ public abstract class AbstractQueuedLongSynchronizer
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long stateOffset;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long waitStatusOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long HEAD;
+ private static final long TAIL;
static {
try {
- stateOffset = unsafe.objectFieldOffset
+ STATE = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("state"));
- headOffset = unsafe.objectFieldOffset
+ HEAD = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("head"));
- tailOffset = unsafe.objectFieldOffset
+ TAIL = U.objectFieldOffset
(AbstractQueuedLongSynchronizer.class.getDeclaredField("tail"));
- waitStatusOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("waitStatus"));
- nextOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
- } catch (Exception ex) { throw new Error(ex); }
+ // Reduce the risk of rare disastrous classloading in first call to
+ // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+ Class> ensureLoaded = LockSupport.class;
}
/**
- * CAS head field. Used only by enq.
+ * Initializes head and tail fields on first contention.
*/
- private final boolean compareAndSetHead(Node update) {
- return unsafe.compareAndSwapObject(this, headOffset, null, update);
+ private final void initializeSyncQueue() {
+ if (U.compareAndSwapObject(this, HEAD, null, new Node()))
+ tail = head;
}
/**
- * CAS tail field. Used only by enq.
+ * CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
- }
-
- /**
- * CAS waitStatus field of a node.
- */
- private static final boolean compareAndSetWaitStatus(Node node,
- int expect,
- int update) {
- return unsafe.compareAndSwapInt(node, waitStatusOffset,
- expect, update);
- }
-
- /**
- * CAS next field of a node.
- */
- private static final boolean compareAndSetNext(Node node,
- Node expect,
- Node update) {
- return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
+ return U.compareAndSwapObject(this, TAIL, expect, update);
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
index d70bd41cc7a..1f36435bb4c 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java
@@ -34,11 +34,11 @@
*/
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
+
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
-import sun.misc.Unsafe;
+import java.util.concurrent.TimeUnit;
/**
* Provides a framework for implementing blocking locks and related
@@ -110,11 +110,11 @@ import sun.misc.Unsafe;
* #setState} and/or {@link #compareAndSetState}:
*
*
- * - {@link #tryAcquire}
- *
- {@link #tryRelease}
- *
- {@link #tryAcquireShared}
- *
- {@link #tryReleaseShared}
- *
- {@link #isHeldExclusively}
+ *
- {@link #tryAcquire}
+ *
- {@link #tryRelease}
+ *
- {@link #tryAcquireShared}
+ *
- {@link #tryReleaseShared}
+ *
- {@link #isHeldExclusively}
*
*
* Each of these methods by default throws {@link
@@ -195,7 +195,7 @@ import sun.misc.Unsafe;
* It also supports conditions and exposes
* one of the instrumentation methods:
*
- * {@code
+ * {@code
* class Mutex implements Lock, java.io.Serializable {
*
* // Our internal helper class
@@ -259,7 +259,7 @@ import sun.misc.Unsafe;
* fire. Because a latch is non-exclusive, it uses the {@code shared}
* acquire and release methods.
*
- * {@code
+ * {@code
* class BooleanLatch {
*
* private static class Sync extends AbstractQueuedSynchronizer {
@@ -383,15 +383,15 @@ public abstract class AbstractQueuedSynchronizer
/** Marker to indicate a node is waiting in exclusive mode */
static final Node EXCLUSIVE = null;
- /** waitStatus value to indicate thread has cancelled */
+ /** waitStatus value to indicate thread has cancelled. */
static final int CANCELLED = 1;
- /** waitStatus value to indicate successor's thread needs unparking */
+ /** waitStatus value to indicate successor's thread needs unparking. */
static final int SIGNAL = -1;
- /** waitStatus value to indicate thread is waiting on condition */
+ /** waitStatus value to indicate thread is waiting on condition. */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
- * unconditionally propagate
+ * unconditionally propagate.
*/
static final int PROPAGATE = -3;
@@ -499,17 +499,49 @@ public abstract class AbstractQueuedSynchronizer
return p;
}
- Node() { // Used to establish initial head or SHARED marker
+ /** Establishes initial head or SHARED marker. */
+ Node() {}
+
+ /** Constructor used by addWaiter. */
+ Node(Node nextWaiter) {
+ this.nextWaiter = nextWaiter;
+ U.putObject(this, THREAD, Thread.currentThread());
}
- Node(Thread thread, Node mode) { // Used by addWaiter
- this.nextWaiter = mode;
- this.thread = thread;
+ /** Constructor used by addConditionWaiter. */
+ Node(int waitStatus) {
+ U.putInt(this, WAITSTATUS, waitStatus);
+ U.putObject(this, THREAD, Thread.currentThread());
}
- Node(Thread thread, int waitStatus) { // Used by Condition
- this.waitStatus = waitStatus;
- this.thread = thread;
+ /** CASes waitStatus field. */
+ final boolean compareAndSetWaitStatus(int expect, int update) {
+ return U.compareAndSwapInt(this, WAITSTATUS, expect, update);
+ }
+
+ /** CASes next field. */
+ final boolean compareAndSetNext(Node expect, Node update) {
+ return U.compareAndSwapObject(this, NEXT, expect, update);
+ }
+
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long NEXT;
+ static final long PREV;
+ private static final long THREAD;
+ private static final long WAITSTATUS;
+ static {
+ try {
+ NEXT = U.objectFieldOffset
+ (Node.class.getDeclaredField("next"));
+ PREV = U.objectFieldOffset
+ (Node.class.getDeclaredField("prev"));
+ THREAD = U.objectFieldOffset
+ (Node.class.getDeclaredField("thread"));
+ WAITSTATUS = U.objectFieldOffset
+ (Node.class.getDeclaredField("waitStatus"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
}
@@ -562,8 +594,7 @@ public abstract class AbstractQueuedSynchronizer
* value was not equal to the expected value.
*/
protected final boolean compareAndSetState(int expect, int update) {
- // See below for intrinsics setup to support this
- return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
+ return U.compareAndSwapInt(this, STATE, expect, update);
}
// Queuing utilities
@@ -573,25 +604,24 @@ public abstract class AbstractQueuedSynchronizer
* rather than to use timed park. A rough estimate suffices
* to improve responsiveness with very short timeouts.
*/
- static final long spinForTimeoutThreshold = 1000L;
+ static final long SPIN_FOR_TIMEOUT_THRESHOLD = 1000L;
/**
* Inserts node into queue, initializing if necessary. See picture above.
* @param node the node to insert
* @return node's predecessor
*/
- private Node enq(final Node node) {
+ private Node enq(Node node) {
for (;;) {
- Node t = tail;
- if (t == null) { // Must initialize
- if (compareAndSetHead(new Node()))
- tail = head;
- } else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return oldTail;
}
+ } else {
+ initializeSyncQueue();
}
}
}
@@ -603,18 +633,20 @@ public abstract class AbstractQueuedSynchronizer
* @return the new node
*/
private Node addWaiter(Node mode) {
- Node node = new Node(Thread.currentThread(), mode);
- // Try the fast path of enq; backup to full enq on failure
- Node pred = tail;
- if (pred != null) {
- node.prev = pred;
- if (compareAndSetTail(pred, node)) {
- pred.next = node;
- return node;
+ Node node = new Node(mode);
+
+ for (;;) {
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return node;
+ }
+ } else {
+ initializeSyncQueue();
}
}
- enq(node);
- return node;
}
/**
@@ -643,7 +675,7 @@ public abstract class AbstractQueuedSynchronizer
*/
int ws = node.waitStatus;
if (ws < 0)
- compareAndSetWaitStatus(node, ws, 0);
+ node.compareAndSetWaitStatus(ws, 0);
/*
* Thread to unpark is held in successor, which is normally
@@ -654,9 +686,9 @@ public abstract class AbstractQueuedSynchronizer
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
- for (Node t = tail; t != null && t != node; t = t.prev)
- if (t.waitStatus <= 0)
- s = t;
+ for (Node p = tail; p != node && p != null; p = p.prev)
+ if (p.waitStatus <= 0)
+ s = p;
}
if (s != null)
LockSupport.unpark(s.thread);
@@ -684,12 +716,12 @@ public abstract class AbstractQueuedSynchronizer
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
- if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
+ if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
- !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
+ !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
@@ -763,18 +795,18 @@ public abstract class AbstractQueuedSynchronizer
// If we are the tail, remove ourselves.
if (node == tail && compareAndSetTail(node, pred)) {
- compareAndSetNext(pred, predNext, null);
+ pred.compareAndSetNext(predNext, null);
} else {
// If successor needs signal, try to set pred's next-link
// so it will get one. Otherwise wake it up to propagate.
int ws;
if (pred != head &&
((ws = pred.waitStatus) == Node.SIGNAL ||
- (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
+ (ws <= 0 && pred.compareAndSetWaitStatus(ws, Node.SIGNAL))) &&
pred.thread != null) {
Node next = node.next;
if (next != null && next.waitStatus <= 0)
- compareAndSetNext(pred, predNext, next);
+ pred.compareAndSetNext(predNext, next);
} else {
unparkSuccessor(node);
}
@@ -815,7 +847,7 @@ public abstract class AbstractQueuedSynchronizer
* need a signal, but don't park yet. Caller will need to
* retry to make sure it cannot acquire before parking.
*/
- compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
+ pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
}
return false;
}
@@ -828,7 +860,7 @@ public abstract class AbstractQueuedSynchronizer
}
/**
- * Convenience method to park and then check if interrupted
+ * Convenience method to park and then check if interrupted.
*
* @return {@code true} if interrupted
*/
@@ -855,7 +887,6 @@ public abstract class AbstractQueuedSynchronizer
* @return {@code true} if interrupted while waiting
*/
final boolean acquireQueued(final Node node, int arg) {
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -863,16 +894,15 @@ public abstract class AbstractQueuedSynchronizer
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -883,23 +913,21 @@ public abstract class AbstractQueuedSynchronizer
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -916,28 +944,28 @@ public abstract class AbstractQueuedSynchronizer
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.EXCLUSIVE);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
- failed = false;
return true;
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -947,7 +975,6 @@ public abstract class AbstractQueuedSynchronizer
*/
private void doAcquireShared(int arg) {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -959,7 +986,6 @@ public abstract class AbstractQueuedSynchronizer
p.next = null; // help GC
if (interrupted)
selfInterrupt();
- failed = false;
return;
}
}
@@ -967,9 +993,9 @@ public abstract class AbstractQueuedSynchronizer
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -980,7 +1006,6 @@ public abstract class AbstractQueuedSynchronizer
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -989,7 +1014,6 @@ public abstract class AbstractQueuedSynchronizer
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return;
}
}
@@ -997,9 +1021,9 @@ public abstract class AbstractQueuedSynchronizer
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -1016,7 +1040,6 @@ public abstract class AbstractQueuedSynchronizer
return false;
final long deadline = System.nanoTime() + nanosTimeout;
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
@@ -1025,22 +1048,23 @@ public abstract class AbstractQueuedSynchronizer
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return true;
}
}
nanosTimeout = deadline - System.nanoTime();
- if (nanosTimeout <= 0L)
+ if (nanosTimeout <= 0L) {
+ cancelAcquire(node);
return false;
+ }
if (shouldParkAfterFailedAcquire(p, node) &&
- nanosTimeout > spinForTimeoutThreshold)
+ nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if (Thread.interrupted())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -1392,7 +1416,7 @@ public abstract class AbstractQueuedSynchronizer
}
/**
- * Version of getFirstQueuedThread called when fastpath fails
+ * Version of getFirstQueuedThread called when fastpath fails.
*/
private Thread fullGetFirstQueuedThread() {
/*
@@ -1472,7 +1496,7 @@ public abstract class AbstractQueuedSynchronizer
*
* An invocation of this method is equivalent to (but may be
* more efficient than):
- *
{@code
+ * {@code
* getFirstQueuedThread() != Thread.currentThread() &&
* hasQueuedThreads()}
*
@@ -1492,7 +1516,7 @@ public abstract class AbstractQueuedSynchronizer
* tryAcquire} method for a fair, reentrant, exclusive mode
* synchronizer might look like this:
*
- * {@code
+ * {@code
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
@@ -1528,8 +1552,7 @@ public abstract class AbstractQueuedSynchronizer
* acquire. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting to acquire
*/
@@ -1554,7 +1577,7 @@ public abstract class AbstractQueuedSynchronizer
* @return the collection of threads
*/
public final Collection getQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
@@ -1572,7 +1595,7 @@ public abstract class AbstractQueuedSynchronizer
* @return the collection of threads
*/
public final Collection getExclusiveQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) {
Thread t = p.thread;
@@ -1592,7 +1615,7 @@ public abstract class AbstractQueuedSynchronizer
* @return the collection of threads
*/
public final Collection getSharedQueuedThreads() {
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) {
Thread t = p.thread;
@@ -1613,10 +1636,9 @@ public abstract class AbstractQueuedSynchronizer
* @return a string identifying this synchronizer, as well as its state
*/
public String toString() {
- int s = getState();
- String q = hasQueuedThreads() ? "non" : "";
- return super.toString() +
- "[State = " + s + ", " + q + "empty queue]";
+ return super.toString()
+ + "[State = " + getState() + ", "
+ + (hasQueuedThreads() ? "non" : "") + "empty queue]";
}
@@ -1650,13 +1672,15 @@ public abstract class AbstractQueuedSynchronizer
* @return true if present
*/
private boolean findNodeFromTail(Node node) {
- Node t = tail;
- for (;;) {
- if (t == node)
+ // We check for node first, since it's likely to be at or near tail.
+ // tail is known to be non-null, so we could re-order to "save"
+ // one null check, but we leave it this way to help the VM.
+ for (Node p = tail;;) {
+ if (p == node)
return true;
- if (t == null)
+ if (p == null)
return false;
- t = t.prev;
+ p = p.prev;
}
}
@@ -1671,7 +1695,7 @@ public abstract class AbstractQueuedSynchronizer
/*
* If cannot change waitStatus, the node has been cancelled.
*/
- if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
+ if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
@@ -1682,7 +1706,7 @@ public abstract class AbstractQueuedSynchronizer
*/
Node p = enq(node);
int ws = p.waitStatus;
- if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
+ if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
@@ -1695,7 +1719,7 @@ public abstract class AbstractQueuedSynchronizer
* @return true if cancelled before the node was signalled
*/
final boolean transferAfterCancelledWait(Node node) {
- if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
+ if (node.compareAndSetWaitStatus(Node.CONDITION, 0)) {
enq(node);
return true;
}
@@ -1717,18 +1741,14 @@ public abstract class AbstractQueuedSynchronizer
* @return previous sync state
*/
final int fullyRelease(Node node) {
- boolean failed = true;
try {
int savedState = getState();
- if (release(savedState)) {
- failed = false;
+ if (release(savedState))
return savedState;
- } else {
- throw new IllegalMonitorStateException();
- }
- } finally {
- if (failed)
- node.waitStatus = Node.CANCELLED;
+ throw new IllegalMonitorStateException();
+ } catch (Throwable t) {
+ node.waitStatus = Node.CANCELLED;
+ throw t;
}
}
@@ -1773,8 +1793,8 @@ public abstract class AbstractQueuedSynchronizer
* given condition associated with this synchronizer. Note that
* because timeouts and interrupts may occur at any time, the
* estimate serves only as an upper bound on the actual number of
- * waiters. This method is designed for use in monitoring of the
- * system state, not for synchronization control.
+ * waiters. This method is designed for use in monitoring system
+ * state, not for synchronization control.
*
* @param condition the condition
* @return the estimated number of waiting threads
@@ -1852,7 +1872,9 @@ public abstract class AbstractQueuedSynchronizer
unlinkCancelledWaiters();
t = lastWaiter;
}
- Node node = new Node(Thread.currentThread(), Node.CONDITION);
+
+ Node node = new Node(Node.CONDITION);
+
if (t == null)
firstWaiter = node;
else
@@ -1960,12 +1982,12 @@ public abstract class AbstractQueuedSynchronizer
/**
* Implements uninterruptible condition wait.
*
- * - Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
*
*/
public final void awaitUninterruptibly() {
@@ -2019,14 +2041,14 @@ public abstract class AbstractQueuedSynchronizer
/**
* Implements interruptible condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled or interrupted.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled or interrupted.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
*
*/
public final void await() throws InterruptedException {
@@ -2051,30 +2073,33 @@ public abstract class AbstractQueuedSynchronizer
/**
* Implements timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
*
*/
public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // awaitNanos(0) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
+ long initialNanos = nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
if (nanosTimeout <= 0L) {
transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -2086,21 +2111,22 @@ public abstract class AbstractQueuedSynchronizer
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
- return deadline - System.nanoTime();
+ long remaining = deadline - System.nanoTime(); // avoid overflow
+ return (remaining <= initialNanos) ? remaining : Long.MIN_VALUE;
}
/**
* Implements absolute timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
- *
- If timed out while blocked in step 4, return false, else true.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If timed out while blocked in step 4, return false, else true.
*
*/
public final boolean awaitUntil(Date deadline)
@@ -2113,7 +2139,7 @@ public abstract class AbstractQueuedSynchronizer
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
- if (System.currentTimeMillis() > abstime) {
+ if (System.currentTimeMillis() >= abstime) {
timedout = transferAfterCancelledWait(node);
break;
}
@@ -2133,15 +2159,15 @@ public abstract class AbstractQueuedSynchronizer
/**
* Implements timed condition wait.
*
- * - If current thread is interrupted, throw InterruptedException.
- *
- Save lock state returned by {@link #getState}.
- *
- Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- *
- Block until signalled, interrupted, or timed out.
- *
- Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- *
- If interrupted while blocked in step 4, throw InterruptedException.
- *
- If timed out while blocked in step 4, return false, else true.
+ *
- If current thread is interrupted, throw InterruptedException.
+ *
- Save lock state returned by {@link #getState}.
+ *
- Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ *
- Block until signalled, interrupted, or timed out.
+ *
- Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ *
- If interrupted while blocked in step 4, throw InterruptedException.
+ *
- If timed out while blocked in step 4, return false, else true.
*
*/
public final boolean await(long time, TimeUnit unit)
@@ -2149,9 +2175,11 @@ public abstract class AbstractQueuedSynchronizer
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
+ // We don't check for nanosTimeout <= 0L here, to allow
+ // await(0, unit) as a way to "yield the lock".
+ final long deadline = System.nanoTime() + nanosTimeout;
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
- final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
@@ -2159,7 +2187,7 @@ public abstract class AbstractQueuedSynchronizer
timedout = transferAfterCancelledWait(node);
break;
}
- if (nanosTimeout >= spinForTimeoutThreshold)
+ if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD)
LockSupport.parkNanos(this, nanosTimeout);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
@@ -2236,7 +2264,7 @@ public abstract class AbstractQueuedSynchronizer
protected final Collection getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- ArrayList list = new ArrayList();
+ ArrayList list = new ArrayList<>();
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
if (w.waitStatus == Node.CONDITION) {
Thread t = w.thread;
@@ -2257,59 +2285,40 @@ public abstract class AbstractQueuedSynchronizer
* are at it, we do the same for other CASable fields (which could
* otherwise be done with atomic field updaters).
*/
- private static final Unsafe unsafe = Unsafe.getUnsafe();
- private static final long stateOffset;
- private static final long headOffset;
- private static final long tailOffset;
- private static final long waitStatusOffset;
- private static final long nextOffset;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long STATE;
+ private static final long HEAD;
+ private static final long TAIL;
static {
try {
- stateOffset = unsafe.objectFieldOffset
+ STATE = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
- headOffset = unsafe.objectFieldOffset
+ HEAD = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
- tailOffset = unsafe.objectFieldOffset
+ TAIL = U.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
- waitStatusOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("waitStatus"));
- nextOffset = unsafe.objectFieldOffset
- (Node.class.getDeclaredField("next"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
- } catch (Exception ex) { throw new Error(ex); }
+ // Reduce the risk of rare disastrous classloading in first call to
+ // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
+ Class> ensureLoaded = LockSupport.class;
}
/**
- * CAS head field. Used only by enq.
+ * Initializes head and tail fields on first contention.
*/
- private final boolean compareAndSetHead(Node update) {
- return unsafe.compareAndSwapObject(this, headOffset, null, update);
+ private final void initializeSyncQueue() {
+ if (U.compareAndSwapObject(this, HEAD, null, new Node()))
+ tail = head;
}
/**
- * CAS tail field. Used only by enq.
+ * CASes tail field.
*/
private final boolean compareAndSetTail(Node expect, Node update) {
- return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
- }
-
- /**
- * CAS waitStatus field of a node.
- */
- private static final boolean compareAndSetWaitStatus(Node node,
- int expect,
- int update) {
- return unsafe.compareAndSwapInt(node, waitStatusOffset,
- expect, update);
- }
-
- /**
- * CAS next field of a node.
- */
- private static final boolean compareAndSetNext(Node node,
- Node expect,
- Node update) {
- return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
+ return U.compareAndSwapObject(this, TAIL, expect, update);
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java
index 06fb8f1cf02..79181fd9dc6 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Condition.java
@@ -34,8 +34,9 @@
*/
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
+
import java.util.Date;
+import java.util.concurrent.TimeUnit;
/**
* {@code Condition} factors out the {@code Object} monitor
@@ -126,7 +127,7 @@ import java.util.Date;
* Note that {@code Condition} instances are just normal objects and can
* themselves be used as the target in a {@code synchronized} statement,
* and can have their own monitor {@link Object#wait wait} and
- * {@link Object#notify notification} methods invoked.
+ * {@link Object#notify notify} methods invoked.
* Acquiring the monitor lock of a {@code Condition} instance, or using its
* monitor methods, has no specified relationship with acquiring the
* {@link Lock} associated with that {@code Condition} or the use of its
@@ -308,7 +309,7 @@ public interface Condition {
* condition still does not hold. Typical uses of this method take
* the following form:
*
- *
{@code
+ * {@code
* boolean aMethod(long timeout, TimeUnit unit) {
* long nanos = unit.toNanos(timeout);
* lock.lock();
@@ -361,7 +362,7 @@ public interface Condition {
* Causes the current thread to wait until it is signalled or interrupted,
* or the specified waiting time elapses. This method is behaviorally
* equivalent to:
- * {@code awaitNanos(unit.toNanos(time)) > 0}
+ * {@code awaitNanos(unit.toNanos(time)) > 0}
*
* @param time the maximum time to wait
* @param unit the time unit of the {@code time} argument
@@ -410,7 +411,7 @@ public interface Condition {
*
* The return value indicates whether the deadline has elapsed,
* which can be used as follows:
- *
{@code
+ * {@code
* boolean aMethod(Date deadline) {
* boolean stillWaiting = true;
* lock.lock();
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java
index 371a6c7c13a..33ac44b8493 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/Lock.java
@@ -34,6 +34,7 @@
*/
package java.util.concurrent.locks;
+
import java.util.concurrent.TimeUnit;
/**
@@ -77,7 +78,7 @@ import java.util.concurrent.TimeUnit;
* methods and statements. In most cases, the following idiom
* should be used:
*
- * {@code
+ * {@code
* Lock l = ...;
* l.lock();
* try {
@@ -121,8 +122,9 @@ import java.util.concurrent.TimeUnit;
* All {@code Lock} implementations must enforce the same
* memory synchronization semantics as provided by the built-in monitor
* lock, as described in
- *
- * The Java Language Specification (17.4 Memory Model):
+ *
+ * Chapter 17 of
+ * The Java™ Language Specification:
*
* - A successful {@code lock} operation has the same memory
* synchronization effects as a successful Lock action.
@@ -240,7 +242,7 @@ public interface Lock {
* immediately with the value {@code false}.
*
*
A typical usage idiom for this method would be:
- *
{@code
+ * {@code
* Lock lock = ...;
* if (lock.tryLock()) {
* try {
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
index 46a0ab597c8..297de583436 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java
@@ -34,7 +34,6 @@
*/
package java.util.concurrent.locks;
-import sun.misc.Unsafe;
/**
* Basic thread blocking primitives for creating locks and other
@@ -47,6 +46,10 @@ import sun.misc.Unsafe;
* it may block. A call to {@code unpark} makes the permit
* available, if it was not already available. (Unlike with Semaphores
* though, permits do not accumulate. There is at most one.)
+ * Reliable usage requires the use of volatile (or atomic) variables
+ * to control when to park or unpark. Orderings of calls to these
+ * methods are maintained with respect to volatile variable accesses,
+ * but not necessarily non-volatile variable accesses.
*
* Methods {@code park} and {@code unpark} provide efficient
* means of blocking and unblocking threads that do not encounter the
@@ -77,7 +80,7 @@ import sun.misc.Unsafe;
* useful for most concurrency control applications. The {@code park}
* method is designed for use only in constructions of the form:
*
- *
{@code
+ * {@code
* while (!canProceed()) { ... LockSupport.park(this); }}
*
* where neither {@code canProceed} nor any other actions prior to the
@@ -87,11 +90,11 @@ import sun.misc.Unsafe;
*
* Sample Usage. Here is a sketch of a first-in-first-out
* non-reentrant lock class:
- *
{@code
+ * {@code
* class FIFOMutex {
* private final AtomicBoolean locked = new AtomicBoolean(false);
* private final Queue waiters
- * = new ConcurrentLinkedQueue();
+ * = new ConcurrentLinkedQueue<>();
*
* public void lock() {
* boolean wasInterrupted = false;
@@ -122,7 +125,7 @@ public class LockSupport {
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
- UNSAFE.putObject(t, parkBlockerOffset, arg);
+ U.putObject(t, PARKBLOCKER, arg);
}
/**
@@ -138,7 +141,7 @@ public class LockSupport {
*/
public static void unpark(Thread thread) {
if (thread != null)
- UNSAFE.unpark(thread);
+ U.unpark(thread);
}
/**
@@ -172,7 +175,7 @@ public class LockSupport {
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- UNSAFE.park(false, 0L);
+ U.park(false, 0L);
setBlocker(t, null);
}
@@ -212,7 +215,7 @@ public class LockSupport {
if (nanos > 0) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- UNSAFE.park(false, nanos);
+ U.park(false, nanos);
setBlocker(t, null);
}
}
@@ -253,7 +256,7 @@ public class LockSupport {
public static void parkUntil(Object blocker, long deadline) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
- UNSAFE.park(true, deadline);
+ U.park(true, deadline);
setBlocker(t, null);
}
@@ -272,7 +275,7 @@ public class LockSupport {
public static Object getBlocker(Thread t) {
if (t == null)
throw new NullPointerException();
- return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
+ return U.getObjectVolatile(t, PARKBLOCKER);
}
/**
@@ -301,7 +304,7 @@ public class LockSupport {
* for example, the interrupt status of the thread upon return.
*/
public static void park() {
- UNSAFE.park(false, 0L);
+ U.park(false, 0L);
}
/**
@@ -335,7 +338,7 @@ public class LockSupport {
*/
public static void parkNanos(long nanos) {
if (nanos > 0)
- UNSAFE.park(false, nanos);
+ U.park(false, nanos);
}
/**
@@ -369,7 +372,7 @@ public class LockSupport {
* to wait until
*/
public static void parkUntil(long deadline) {
- UNSAFE.park(true, deadline);
+ U.park(true, deadline);
}
/**
@@ -379,36 +382,30 @@ public class LockSupport {
static final int nextSecondarySeed() {
int r;
Thread t = Thread.currentThread();
- if ((r = UNSAFE.getInt(t, SECONDARY)) != 0) {
+ if ((r = U.getInt(t, SECONDARY)) != 0) {
r ^= r << 13; // xorshift
r ^= r >>> 17;
r ^= r << 5;
}
else if ((r = java.util.concurrent.ThreadLocalRandom.current().nextInt()) == 0)
r = 1; // avoid zero
- UNSAFE.putInt(t, SECONDARY, r);
+ U.putInt(t, SECONDARY, r);
return r;
}
// Hotspot implementation via intrinsics API
- private static final sun.misc.Unsafe UNSAFE;
- private static final long parkBlockerOffset;
- private static final long SEED;
- private static final long PROBE;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long PARKBLOCKER;
private static final long SECONDARY;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class> tk = Thread.class;
- parkBlockerOffset = UNSAFE.objectFieldOffset
- (tk.getDeclaredField("parkBlocker"));
- SEED = UNSAFE.objectFieldOffset
- (tk.getDeclaredField("threadLocalRandomSeed"));
- PROBE = UNSAFE.objectFieldOffset
- (tk.getDeclaredField("threadLocalRandomProbe"));
- SECONDARY = UNSAFE.objectFieldOffset
- (tk.getDeclaredField("threadLocalRandomSecondarySeed"));
- } catch (Exception ex) { throw new Error(ex); }
+ PARKBLOCKER = U.objectFieldOffset
+ (Thread.class.getDeclaredField("parkBlocker"));
+ SECONDARY = U.objectFieldOffset
+ (Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
+ } catch (ReflectiveOperationException e) {
+ throw new Error(e);
+ }
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
index 40b0f344238..00fb84530b9 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReadWriteLock.java
@@ -38,9 +38,9 @@ package java.util.concurrent.locks;
/**
* A {@code ReadWriteLock} maintains a pair of associated {@link
* Lock locks}, one for read-only operations and one for writing.
- * The {@link #readLock read lock} may be held simultaneously by
- * multiple reader threads, so long as there are no writers. The
- * {@link #writeLock write lock} is exclusive.
+ * The {@linkplain #readLock read lock} may be held simultaneously
+ * by multiple reader threads, so long as there are no writers.
+ * The {@linkplain #writeLock write lock} is exclusive.
*
* All {@code ReadWriteLock} implementations must guarantee that
* the memory synchronization effects of {@code writeLock} operations
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java
index 06c7e61ab65..9df25057041 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantLock.java
@@ -34,8 +34,9 @@
*/
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
+
import java.util.Collection;
+import java.util.concurrent.TimeUnit;
/**
* A reentrant mutual exclusion {@link Lock} with the same basic
@@ -72,7 +73,7 @@ import java.util.Collection;
* follow a call to {@code lock} with a {@code try} block, most
* typically in a before/after construction such as:
*
- *
{@code
+ * {@code
* class X {
* private final ReentrantLock lock = new ReentrantLock();
* // ...
@@ -378,7 +379,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* method. If you want a timed {@code tryLock} that does permit barging on
* a fair lock then combine the timed and un-timed forms together:
*
- * {@code
+ * {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -484,7 +485,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * - Waiting threads are signalled in FIFO order.
+ *
- Waiting threads are signalled in FIFO order.
*
*
- The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
@@ -511,7 +512,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* not be entered with the lock already held then we can assert that
* fact:
*
- *
{@code
+ * {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -541,7 +542,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* debugging and testing. For example, a method that should only be
* called while a lock is held can assert that this is the case:
*
- * {@code
+ * {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -555,7 +556,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
* It can also be used to ensure that a reentrant lock is used
* in a non-reentrant manner, for example:
*
- *
{@code
+ * {@code
* class X {
* ReentrantLock lock = new ReentrantLock();
* // ...
@@ -646,12 +647,11 @@ public class ReentrantLock implements Lock, java.io.Serializable {
}
/**
- * Returns an estimate of the number of threads waiting to
- * acquire this lock. The value is only an estimate because the number of
+ * Returns an estimate of the number of threads waiting to acquire
+ * this lock. The value is only an estimate because the number of
* threads may change dynamically while this method traverses
* internal data structures. This method is designed for use in
- * monitoring of the system state, not for synchronization
- * control.
+ * monitoring system state, not for synchronization control.
*
* @return the estimated number of threads waiting for this lock
*/
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
index 67ef53a4f22..c41b4285a67 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/ReentrantReadWriteLock.java
@@ -34,8 +34,9 @@
*/
package java.util.concurrent.locks;
-import java.util.concurrent.TimeUnit;
+
import java.util.Collection;
+import java.util.concurrent.TimeUnit;
/**
* An implementation of {@link ReadWriteLock} supporting similar
@@ -51,14 +52,16 @@ import java.util.Collection;
*
*
* - Non-fair mode (default)
- *
- When constructed as non-fair (the default), the order of entry
+ *
-
+ * When constructed as non-fair (the default), the order of entry
* to the read and write lock is unspecified, subject to reentrancy
* constraints. A nonfair lock that is continuously contended may
* indefinitely postpone one or more reader or writer threads, but
* will normally have higher throughput than a fair lock.
*
*
- Fair mode
- *
- When constructed as fair, threads contend for entry using an
+ *
-
+ * When constructed as fair, threads contend for entry using an
* approximately arrival-order policy. When the currently held lock
* is released, either the longest-waiting single writer thread will
* be assigned the write lock, or if there is a group of reader threads
@@ -173,9 +176,9 @@ import java.util.Collection;
* is a class using a TreeMap that is expected to be large and
* concurrently accessed.
*
- *
{@code
+ * {@code
* class RWDictionary {
- * private final Map m = new TreeMap();
+ * private final Map m = new TreeMap<>();
* private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
* private final Lock r = rwl.readLock();
* private final Lock w = rwl.writeLock();
@@ -263,17 +266,17 @@ public class ReentrantReadWriteLock
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1;
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;
- /** Returns the number of shared holds represented in count */
+ /** Returns the number of shared holds represented in count. */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
- /** Returns the number of exclusive holds represented in count */
+ /** Returns the number of exclusive holds represented in count. */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
/**
* A counter for per-thread read hold counts.
- * Maintained as a ThreadLocal; cached in cachedHoldCounter
+ * Maintained as a ThreadLocal; cached in cachedHoldCounter.
*/
static final class HoldCounter {
- int count = 0;
+ int count; // initially 0
// Use id, not reference, to avoid garbage retention
final long tid = getThreadId(Thread.currentThread());
}
@@ -330,7 +333,7 @@ public class ReentrantReadWriteLock
* This allows tracking of read holds for uncontended read
* locks to be very cheap.
*/
- private transient Thread firstReader = null;
+ private transient Thread firstReader;
private transient int firstReaderHoldCount;
Sync() {
@@ -703,7 +706,7 @@ public class ReentrantReadWriteLock
private final Sync sync;
/**
- * Constructor for use by subclasses
+ * Constructor for use by subclasses.
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
@@ -814,7 +817,7 @@ public class ReentrantReadWriteLock
* permit barging on a fair lock then combine the timed and
* un-timed forms together:
*
- *
{@code
+ * {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -874,7 +877,12 @@ public class ReentrantReadWriteLock
* Attempts to release this lock.
*
* If the number of readers is now zero then the lock
- * is made available for write lock attempts.
+ * is made available for write lock attempts. If the current
+ * thread does not hold this lock then {@link
+ * IllegalMonitorStateException} is thrown.
+ *
+ * @throws IllegalMonitorStateException if the current thread
+ * does not hold this lock
*/
public void unlock() {
sync.releaseShared(1);
@@ -912,7 +920,7 @@ public class ReentrantReadWriteLock
private final Sync sync;
/**
- * Constructor for use by subclasses
+ * Constructor for use by subclasses.
*
* @param lock the outer lock object
* @throws NullPointerException if the lock is null
@@ -1026,7 +1034,7 @@ public class ReentrantReadWriteLock
* by the current thread, or the write lock was already held
* by the current thread; and {@code false} otherwise.
*/
- public boolean tryLock( ) {
+ public boolean tryLock() {
return sync.tryWriteLock();
}
@@ -1046,7 +1054,7 @@ public class ReentrantReadWriteLock
* that does permit barging on a fair lock then combine the
* timed and un-timed forms together:
*
- *
{@code
+ * {@code
* if (lock.tryLock() ||
* lock.tryLock(timeout, unit)) {
* ...
@@ -1161,7 +1169,7 @@ public class ReentrantReadWriteLock
* InterruptedException} will be thrown, and the thread's
* interrupted status will be cleared.
*
- * - Waiting threads are signalled in FIFO order.
+ *
- Waiting threads are signalled in FIFO order.
*
*
- The ordering of lock reacquisition for threads returning
* from waiting methods is the same as for threads initially
@@ -1369,7 +1377,7 @@ public class ReentrantReadWriteLock
* either the read or write lock. The value is only an estimate
* because the number of threads may change dynamically while this
* method traverses internal data structures. This method is
- * designed for use in monitoring of the system state, not for
+ * designed for use in monitoring system state, not for
* synchronization control.
*
* @return the estimated number of threads waiting for this lock
@@ -1489,19 +1497,17 @@ public class ReentrantReadWriteLock
* ways that do not preserve unique mappings.
*/
static final long getThreadId(Thread thread) {
- return UNSAFE.getLongVolatile(thread, TID_OFFSET);
+ return U.getLongVolatile(thread, TID);
}
// Unsafe mechanics
- private static final sun.misc.Unsafe UNSAFE;
- private static final long TID_OFFSET;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
+ private static final long TID;
static {
try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- Class> tk = Thread.class;
- TID_OFFSET = UNSAFE.objectFieldOffset
- (tk.getDeclaredField("tid"));
- } catch (Exception e) {
+ TID = U.objectFieldOffset
+ (Thread.class.getDeclaredField("tid"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
index 5fe20b6a578..d6895996d91 100644
--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/StampedLock.java
@@ -36,10 +36,6 @@
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.LockSupport;
/**
* A capability-based lock with three modes for controlling read/write
@@ -58,12 +54,12 @@ import java.util.concurrent.locks.LockSupport;
* in method {@link #unlockWrite} to release the lock. Untimed and
* timed versions of {@code tryWriteLock} are also provided. When
* the lock is held in write mode, no read locks may be obtained,
- * and all optimistic read validations will fail.
+ * and all optimistic read validations will fail.
*
* - Reading. Method {@link #readLock} possibly blocks
* waiting for non-exclusive access, returning a stamp that can be
* used in method {@link #unlockRead} to release the lock. Untimed
- * and timed versions of {@code tryReadLock} are also provided.
+ * and timed versions of {@code tryReadLock} are also provided.
*
* - Optimistic Reading. Method {@link #tryOptimisticRead}
* returns a non-zero stamp only if the lock is not currently held
@@ -81,7 +77,7 @@ import java.util.concurrent.locks.LockSupport;
* invoke method {@code validate()}. For example, such steps are
* typically required when first reading an object or array
* reference, and then accessing one of its fields, elements or
- * methods.
+ * methods.
*
*
*
@@ -132,7 +128,7 @@ import java.util.concurrent.locks.LockSupport;
* not strictly needed here because no exceptions can occur in their
* bodies.
*
- * {@code
+ * {@code
* class Point {
* private double x, y;
* private final StampedLock sl = new StampedLock();
@@ -542,7 +538,7 @@ public class StampedLock implements java.io.Serializable {
WNode h;
if (state != stamp || (stamp & WBIT) == 0L)
throw new IllegalMonitorStateException();
- state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
+ U.putLongVolatile(this, STATE, (stamp += WBIT) == 0L ? ORIGIN : stamp);
if ((h = whead) != null && h.status != 0)
release(h);
}
@@ -589,7 +585,7 @@ public class StampedLock implements java.io.Serializable {
else if (m == WBIT) {
if (a != m)
break;
- state = (s += WBIT) == 0L ? ORIGIN : s;
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
if ((h = whead) != null && h.status != 0)
release(h);
return;
@@ -610,7 +606,7 @@ public class StampedLock implements java.io.Serializable {
}
/**
- * If the lock state matches the given stamp, performs one of
+ * If the lock state matches the given stamp, atomically performs one of
* the following actions. If the stamp represents holding a write
* lock, returns it. Or, if a read lock, if the write lock is
* available, releases the read lock and returns a write stamp.
@@ -647,7 +643,7 @@ public class StampedLock implements java.io.Serializable {
}
/**
- * If the lock state matches the given stamp, performs one of
+ * If the lock state matches the given stamp, atomically performs one of
* the following actions. If the stamp represents holding a write
* lock, releases it and obtains a read lock. Or, if a read lock,
* returns it. Or, if an optimistic read, acquires a read lock and
@@ -673,7 +669,7 @@ public class StampedLock implements java.io.Serializable {
else if (m == WBIT) {
if (a != m)
break;
- state = next = s + (WBIT + RUNIT);
+ U.putLongVolatile(this, STATE, next = s + (WBIT + RUNIT));
if ((h = whead) != null && h.status != 0)
release(h);
return next;
@@ -687,7 +683,7 @@ public class StampedLock implements java.io.Serializable {
}
/**
- * If the lock state matches the given stamp then, if the stamp
+ * If the lock state matches the given stamp then, atomically, if the stamp
* represents holding a lock, releases it and returns an
* observation stamp. Or, if an optimistic read, returns it if
* validated. This method returns zero in all other cases, and so
@@ -710,7 +706,8 @@ public class StampedLock implements java.io.Serializable {
else if (m == WBIT) {
if (a != m)
break;
- state = next = (s += WBIT) == 0L ? ORIGIN : s;
+ U.putLongVolatile(this, STATE,
+ next = (s += WBIT) == 0L ? ORIGIN : s);
if ((h = whead) != null && h.status != 0)
release(h);
return next;
@@ -740,7 +737,7 @@ public class StampedLock implements java.io.Serializable {
public boolean tryUnlockWrite() {
long s; WNode h;
if (((s = state) & WBIT) != 0L) {
- state = (s += WBIT) == 0L ? ORIGIN : s;
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
if ((h = whead) != null && h.status != 0)
release(h);
return true;
@@ -923,7 +920,7 @@ public class StampedLock implements java.io.Serializable {
WNode h; long s;
if (((s = state) & WBIT) == 0L)
throw new IllegalMonitorStateException();
- state = (s += WBIT) == 0L ? ORIGIN : s;
+ U.putLongVolatile(this, STATE, (s += WBIT) == 0L ? ORIGIN : s);
if ((h = whead) != null && h.status != 0)
release(h);
}
@@ -948,7 +945,7 @@ public class StampedLock implements java.io.Serializable {
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
- state = ORIGIN; // reset to unlocked state
+ U.putLongVolatile(this, STATE, ORIGIN); // reset to unlocked state
}
// internals
@@ -966,7 +963,7 @@ public class StampedLock implements java.io.Serializable {
if ((s & ABITS) == RFULL) {
if (U.compareAndSwapLong(this, STATE, s, s | RBITS)) {
++readerOverflow;
- state = s;
+ U.putLongVolatile(this, STATE, s);
return s;
}
}
@@ -993,8 +990,8 @@ public class StampedLock implements java.io.Serializable {
}
else
next = s - RUNIT;
- state = next;
- return next;
+ U.putLongVolatile(this, STATE, next);
+ return next;
}
}
else if ((LockSupport.nextSecondarySeed() &
@@ -1062,6 +1059,7 @@ public class StampedLock implements java.io.Serializable {
}
}
+ boolean wasInterrupted = false;
for (int spins = -1;;) {
WNode h, np, pp; int ps;
if ((h = whead) == p) {
@@ -1076,6 +1074,8 @@ public class StampedLock implements java.io.Serializable {
ns = s + WBIT)) {
whead = node;
node.prev = null;
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
return ns;
}
}
@@ -1119,8 +1119,11 @@ public class StampedLock implements java.io.Serializable {
U.park(false, time); // emulate LockSupport.park
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
- if (interruptible && Thread.interrupted())
- return cancelWaiter(node, node, true);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, node, true);
+ wasInterrupted = true;
+ }
}
}
}
@@ -1136,6 +1139,7 @@ public class StampedLock implements java.io.Serializable {
* @return next state, or INTERRUPTED
*/
private long acquireRead(boolean interruptible, long deadline) {
+ boolean wasInterrupted = false;
WNode node = null, p;
for (int spins = -1;;) {
WNode h;
@@ -1143,8 +1147,11 @@ public class StampedLock implements java.io.Serializable {
for (long m, s, ns;;) {
if ((m = (s = state) & ABITS) < RFULL ?
U.compareAndSwapLong(this, STATE, s, ns = s + RUNIT) :
- (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L))
+ (m < WBIT && (ns = tryIncReaderOverflow(s)) != 0L)) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
return ns;
+ }
else if (m >= WBIT) {
if (spins > 0) {
if (LockSupport.nextSecondarySeed() >= 0)
@@ -1193,8 +1200,11 @@ public class StampedLock implements java.io.Serializable {
U.compareAndSwapLong(this, STATE, s,
ns = s + RUNIT) :
(m < WBIT &&
- (ns = tryIncReaderOverflow(s)) != 0L))
+ (ns = tryIncReaderOverflow(s)) != 0L)) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
return ns;
+ }
} while (m < WBIT);
}
if (whead == h && p.prev == pp) {
@@ -1205,8 +1215,11 @@ public class StampedLock implements java.io.Serializable {
}
if (deadline == 0L)
time = 0L;
- else if ((time = deadline - System.nanoTime()) <= 0L)
+ else if ((time = deadline - System.nanoTime()) <= 0L) {
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
return cancelWaiter(node, p, false);
+ }
Thread wt = Thread.currentThread();
U.putObject(wt, PARKBLOCKER, this);
node.thread = wt;
@@ -1215,8 +1228,11 @@ public class StampedLock implements java.io.Serializable {
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
- if (interruptible && Thread.interrupted())
- return cancelWaiter(node, p, true);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, p, true);
+ wasInterrupted = true;
+ }
}
}
}
@@ -1243,6 +1259,8 @@ public class StampedLock implements java.io.Serializable {
(w = c.thread) != null)
U.unpark(w);
}
+ if (wasInterrupted)
+ Thread.currentThread().interrupt();
return ns;
}
else if (m >= WBIT &&
@@ -1286,8 +1304,11 @@ public class StampedLock implements java.io.Serializable {
U.park(false, time);
node.thread = null;
U.putObject(wt, PARKBLOCKER, null);
- if (interruptible && Thread.interrupted())
- return cancelWaiter(node, node, true);
+ if (Thread.interrupted()) {
+ if (interruptible)
+ return cancelWaiter(node, node, true);
+ wasInterrupted = true;
+ }
}
}
}
@@ -1377,7 +1398,7 @@ public class StampedLock implements java.io.Serializable {
}
// Unsafe mechanics
- private static final sun.misc.Unsafe U;
+ private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();
private static final long STATE;
private static final long WHEAD;
private static final long WTAIL;
@@ -1388,26 +1409,23 @@ public class StampedLock implements java.io.Serializable {
static {
try {
- U = sun.misc.Unsafe.getUnsafe();
- Class> k = StampedLock.class;
- Class> wk = WNode.class;
STATE = U.objectFieldOffset
- (k.getDeclaredField("state"));
+ (StampedLock.class.getDeclaredField("state"));
WHEAD = U.objectFieldOffset
- (k.getDeclaredField("whead"));
+ (StampedLock.class.getDeclaredField("whead"));
WTAIL = U.objectFieldOffset
- (k.getDeclaredField("wtail"));
- WSTATUS = U.objectFieldOffset
- (wk.getDeclaredField("status"));
- WNEXT = U.objectFieldOffset
- (wk.getDeclaredField("next"));
- WCOWAIT = U.objectFieldOffset
- (wk.getDeclaredField("cowait"));
- Class> tk = Thread.class;
- PARKBLOCKER = U.objectFieldOffset
- (tk.getDeclaredField("parkBlocker"));
+ (StampedLock.class.getDeclaredField("wtail"));
- } catch (Exception e) {
+ 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"));
+ } catch (ReflectiveOperationException e) {
throw new Error(e);
}
}
diff --git a/jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java b/jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java
new file mode 100644
index 00000000000..823f72e50c8
--- /dev/null
+++ b/jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java
@@ -0,0 +1,412 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/*
+ * @test
+ * @bug 4486658
+ * @run main/timeout=7200 CheckedLockLoops
+ * @summary basic safety and liveness of ReentrantLocks, and other locks based on them
+ */
+
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+import java.util.*;
+
+public final class CheckedLockLoops {
+ static final ExecutorService pool = Executors.newCachedThreadPool();
+ static final LoopHelpers.SimpleRandom rng = new LoopHelpers.SimpleRandom();
+ static boolean print = false;
+ static boolean doBuiltin = false;
+
+ public static void main(String[] args) throws Exception {
+ int maxThreads = 5;
+ int iters = 100000;
+
+ if (args.length > 0)
+ maxThreads = Integer.parseInt(args[0]);
+
+ rng.setSeed(3122688L);
+
+ print = false;
+ System.out.println("Warmup...");
+ oneTest(3, 10000);
+ Thread.sleep(1000);
+ oneTest(2, 10000);
+ Thread.sleep(100);
+ oneTest(1, 100000);
+ Thread.sleep(100);
+ oneTest(1, 100000);
+ Thread.sleep(1000);
+ print = true;
+
+ for (int i = 1; i <= maxThreads; i += (i+1) >>> 1) {
+ System.out.println("Threads:" + i);
+ oneTest(i, iters / i);
+ Thread.sleep(100);
+ }
+ pool.shutdown();
+ if (! pool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS))
+ throw new Error();
+ }
+
+ static void oneTest(int nthreads, int iters) throws Exception {
+ int v = rng.next();
+ if (doBuiltin) {
+ if (print)
+ System.out.print("builtin lock ");
+ new BuiltinLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+ }
+
+ if (print)
+ System.out.print("ReentrantLock ");
+ new ReentrantLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("Mutex ");
+ new MutexLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("ReentrantWriteLock ");
+ new ReentrantWriteLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("ReentrantReadWriteLock");
+ new ReentrantReadWriteLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("Semaphore ");
+ new SemaphoreLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("fair Semaphore ");
+ new FairSemaphoreLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("FairReentrantLock ");
+ new FairReentrantLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("FairRWriteLock ");
+ new FairReentrantWriteLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+
+ if (print)
+ System.out.print("FairRReadWriteLock ");
+ new FairReentrantReadWriteLockLoop().test(v, nthreads, iters);
+ Thread.sleep(10);
+ }
+
+ abstract static class LockLoop implements Runnable {
+ int value;
+ int checkValue;
+ int iters;
+ volatile int result;
+ final LoopHelpers.BarrierTimer timer = new LoopHelpers.BarrierTimer();
+ CyclicBarrier barrier;
+
+ final int setValue(int v) {
+ checkValue = v ^ 0x55555555;
+ value = v;
+ return v;
+ }
+
+ final int getValue() {
+ int v = value;
+ if (checkValue != ~(v ^ 0xAAAAAAAA))
+ throw new Error("lock protection failure");
+ return v;
+ }
+
+ final void test(int initialValue, int nthreads, int iters) throws Exception {
+ setValue(initialValue);
+ this.iters = iters;
+ barrier = new CyclicBarrier(nthreads+1, timer);
+ for (int i = 0; i < nthreads; ++i)
+ pool.execute(this);
+ barrier.await();
+ barrier.await();
+ long time = timer.getTime();
+ if (print) {
+ long tpi = time / (iters * nthreads);
+ System.out.print("\t" + LoopHelpers.rightJustify(tpi) + " ns per update");
+ // double secs = (double)(time) / 1000000000.0;
+ // System.out.print("\t " + secs + "s run time");
+ System.out.println();
+ }
+
+ if (result == 0) // avoid overoptimization
+ System.out.println("useless result: " + result);
+ }
+ abstract int loop(int n);
+ public final void run() {
+ try {
+ barrier.await();
+ result += loop(iters);
+ barrier.await();
+ }
+ catch (Exception ie) {
+ return;
+ }
+ }
+
+ }
+
+ private static class BuiltinLockLoop extends LockLoop {
+ final int loop(int n) {
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ synchronized (this) {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class ReentrantLockLoop extends LockLoop {
+ private final ReentrantLock lock = new ReentrantLock();
+ final int loop(int n) {
+ final ReentrantLock lock = this.lock;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ lock.lock();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ lock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class MutexLoop extends LockLoop {
+ private final Mutex lock = new Mutex();
+ final int loop(int n) {
+ final Mutex lock = this.lock;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ lock.lock();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ lock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class FairReentrantLockLoop extends LockLoop {
+ private final ReentrantLock lock = new ReentrantLock(true);
+ final int loop(int n) {
+ final ReentrantLock lock = this.lock;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ lock.lock();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ lock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class ReentrantWriteLockLoop extends LockLoop {
+ private final Lock lock = new ReentrantReadWriteLock().writeLock();
+ final int loop(int n) {
+ final Lock lock = this.lock;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ lock.lock();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ lock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class FairReentrantWriteLockLoop extends LockLoop {
+ final Lock lock = new ReentrantReadWriteLock(true).writeLock();
+ final int loop(int n) {
+ final Lock lock = this.lock;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ lock.lock();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ lock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class SemaphoreLoop extends LockLoop {
+ private final Semaphore sem = new Semaphore(1, false);
+ final int loop(int n) {
+ final Semaphore sem = this.sem;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ sem.acquireUninterruptibly();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ sem.release();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+ private static class FairSemaphoreLoop extends LockLoop {
+ private final Semaphore sem = new Semaphore(1, true);
+ final int loop(int n) {
+ final Semaphore sem = this.sem;
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ sem.acquireUninterruptibly();
+ try {
+ x = setValue(LoopHelpers.compute1(getValue()));
+ }
+ finally {
+ sem.release();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ return sum;
+ }
+ }
+
+ private static class ReentrantReadWriteLockLoop extends LockLoop {
+ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+ final int loop(int n) {
+ final Lock rlock = lock.readLock();
+ final Lock wlock = lock.writeLock();
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ if ((n & 16) != 0) {
+ rlock.lock();
+ try {
+ x = LoopHelpers.compute1(getValue());
+ x = LoopHelpers.compute2(x);
+ }
+ finally {
+ rlock.unlock();
+ }
+ }
+ else {
+ wlock.lock();
+ try {
+ setValue(x);
+ }
+ finally {
+ wlock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ }
+ return sum;
+ }
+
+ }
+
+ private static class FairReentrantReadWriteLockLoop extends LockLoop {
+ private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);
+ final int loop(int n) {
+ final Lock rlock = lock.readLock();
+ final Lock wlock = lock.writeLock();
+ int sum = 0;
+ int x = 0;
+ while (n-- > 0) {
+ if ((n & 16) != 0) {
+ rlock.lock();
+ try {
+ x = LoopHelpers.compute1(getValue());
+ x = LoopHelpers.compute2(x);
+ }
+ finally {
+ rlock.unlock();
+ }
+ }
+ else {
+ wlock.lock();
+ try {
+ setValue(x);
+ }
+ finally {
+ wlock.unlock();
+ }
+ sum += LoopHelpers.compute2(x);
+ }
+ }
+ return sum;
+ }
+
+ }
+}
diff --git a/jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java b/jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java
new file mode 100644
index 00000000000..d9859f30066
--- /dev/null
+++ b/jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java
@@ -0,0 +1,129 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/**
+ * Misc utilities in JSR166 performance tests
+ */
+
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
+
+class LoopHelpers {
+
+ // Some mindless computation to do between synchronizations...
+
+ /**
+ * generates 32 bit pseudo-random numbers.
+ * Adapted from http://www.snippets.org
+ */
+ public static int compute1(int x) {
+ int lo = 16807 * (x & 0xFFFF);
+ int hi = 16807 * (x >>> 16);
+ lo += (hi & 0x7FFF) << 16;
+ if ((lo & 0x80000000) != 0) {
+ lo &= 0x7fffffff;
+ ++lo;
+ }
+ lo += hi >>> 15;
+ if (lo == 0 || (lo & 0x80000000) != 0) {
+ lo &= 0x7fffffff;
+ ++lo;
+ }
+ return lo;
+ }
+
+ /**
+ * Computes a linear congruential random number a random number
+ * of times.
+ */
+ public static int compute2(int x) {
+ int loops = (x >>> 4) & 7;
+ while (loops-- > 0) {
+ x = (x * 2147483647) % 16807;
+ }
+ return x;
+ }
+
+ /**
+ * An actually useful random number generator, but unsynchronized.
+ * Basically same as java.util.Random.
+ */
+ public static class SimpleRandom {
+ private static final long multiplier = 0x5DEECE66DL;
+ private static final long addend = 0xBL;
+ private static final long mask = (1L << 48) - 1;
+ static final AtomicLong seq = new AtomicLong(1);
+ private long seed = System.nanoTime() + seq.getAndIncrement();
+
+ public void setSeed(long s) {
+ seed = s;
+ }
+
+ public int next() {
+ long nextseed = (seed * multiplier + addend) & mask;
+ seed = nextseed;
+ return ((int)(nextseed >>> 17)) & 0x7FFFFFFF;
+ }
+ }
+
+ public static class BarrierTimer implements Runnable {
+ public volatile long startTime;
+ public volatile long endTime;
+ public void run() {
+ long t = System.nanoTime();
+ if (startTime == 0)
+ startTime = t;
+ else
+ endTime = t;
+ }
+ public void clear() {
+ startTime = 0;
+ endTime = 0;
+ }
+ public long getTime() {
+ return endTime - startTime;
+ }
+ }
+
+ public static String rightJustify(long n) {
+ // There's probably a better way to do this...
+ String field = " ";
+ String num = Long.toString(n);
+ if (num.length() >= field.length())
+ return num;
+ StringBuffer b = new StringBuffer(field);
+ b.replace(b.length()-num.length(), b.length(), num);
+ return b.toString();
+ }
+
+}
diff --git a/jdk/test/java/util/concurrent/locks/Lock/Mutex.java b/jdk/test/java/util/concurrent/locks/Lock/Mutex.java
new file mode 100644
index 00000000000..f00148a8995
--- /dev/null
+++ b/jdk/test/java/util/concurrent/locks/Lock/Mutex.java
@@ -0,0 +1,82 @@
+/*
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * This file is available under and governed by the GNU General Public
+ * License version 2 only, as published by the Free Software Foundation.
+ * However, the following notice accompanied the original version of this
+ * file:
+ *
+ * Written by Doug Lea with assistance from members of JCP JSR-166
+ * Expert Group and released to the public domain, as explained at
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.locks.*;
+import java.util.concurrent.atomic.*;
+import java.io.*;
+
+/**
+ * A sample user extension of AbstractQueuedSynchronizer.
+ */
+public class Mutex implements Lock, java.io.Serializable {
+ private static class Sync extends AbstractQueuedSynchronizer {
+ public boolean isHeldExclusively() { return getState() == 1; }
+
+ public boolean tryAcquire(int acquires) {
+ assert acquires == 1; // Does not use multiple acquires
+ return compareAndSetState(0, 1);
+ }
+
+ public boolean tryRelease(int releases) {
+ setState(0);
+ return true;
+ }
+
+ Condition newCondition() { return new ConditionObject(); }
+
+ private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ setState(0); // reset to unlocked state
+ }
+ }
+
+ private final Sync sync = new Sync();
+ public void lock() {
+ sync.acquire(1);
+ }
+ public boolean tryLock() {
+ return sync.tryAcquire(1);
+ }
+ public void lockInterruptibly() throws InterruptedException {
+ sync.acquireInterruptibly(1);
+ }
+ public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
+ return sync.tryAcquireNanos(1, unit.toNanos(timeout));
+ }
+ public void unlock() { sync.release(1); }
+ public Condition newCondition() { return sync.newCondition(); }
+ public boolean isLocked() { return sync.isHeldExclusively(); }
+ public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
+}
diff --git a/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java b/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java
index 62749bf6ba3..8d6f769cd67 100644
--- a/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java
+++ b/jdk/test/java/util/concurrent/locks/Lock/TimedAcquireLeak.java
@@ -28,14 +28,32 @@
* @author Martin Buchholz
*/
-// Note: this file is now out of sync with the jsr166 CVS repository due to the fix for 7092140
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+import static java.util.concurrent.TimeUnit.SECONDS;
-import java.util.*;
-import java.util.regex.*;
-import java.util.concurrent.*;
-import java.util.concurrent.locks.*;
-import static java.util.concurrent.TimeUnit.*;
-import java.io.*;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.lang.ref.WeakReference;
+import java.util.Random;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.CyclicBarrier;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.locks.ReentrantLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class TimedAcquireLeak {
static String javahome() {
@@ -96,18 +114,34 @@ public class TimedAcquireLeak {
Callable callable) throws Throwable {
p.getInputStream().read();
T result = callable.call();
- OutputStream os = p.getOutputStream();
- os.write((byte)'\n'); os.flush();
+ sendByte(p.getOutputStream());
return result;
}
+ /** No guarantees, but effective in practice. */
+ private static void forceFullGc() {
+ CountDownLatch finalizeDone = new CountDownLatch(1);
+ WeakReference> ref = new WeakReference