--- a/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Tue Oct 13 16:25:10 2015 -0700
+++ b/jdk/src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java Tue Oct 13 16:35:22 2015 -0700
@@ -34,11 +34,12 @@
*/
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;
+import java.util.concurrent.locks.AbstractQueuedSynchronizer.Node;
/**
* A version of {@link AbstractQueuedSynchronizer} in
@@ -77,221 +78,6 @@
protected AbstractQueuedLongSynchronizer() { }
/**
- * Wait queue node class.
- *
- * <p>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.
- *
- * <p>To enqueue into a CLH lock, you atomically splice it in as new
- * tail. To dequeue, you just set the head field.
- * <pre>
- * +------+ prev +-----+ +-----+
- * head | | <---- | | <---- | | tail
- * +------+ +-----+ +-----+
- * </pre>
- *
- * <p>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.
- *
- * <p>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/
- *
- * <p>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.)
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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.
- *
- * <p>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:
* If head exists, its waitStatus is guaranteed not to be
@@ -325,7 +111,9 @@
* @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 @@
* 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 @@
* 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;
+ Node oldTail = tail;
+ if (oldTail != null) {
+ U.putObject(node, Node.PREV, oldTail);
+ if (compareAndSetTail(oldTail, node)) {
+ oldTail.next = node;
+ return oldTail;
+ }
} else {
- node.prev = t;
- if (compareAndSetTail(t, node)) {
- t.next = node;
- return t;
- }
+ initializeSyncQueue();
}
}
}
@@ -381,18 +167,20 @@
* @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 @@
*/
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 @@
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 @@
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 @@
// 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 @@
* 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 @@
}
/**
- * 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 @@
* @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 @@
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 @@
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 @@
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 @@
*/
private void doAcquireShared(long arg) {
final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
@@ -737,7 +520,6 @@
p.next = null; // help GC
if (interrupted)
selfInterrupt();
- failed = false;
return;
}
}
@@ -745,9 +527,9 @@
parkAndCheckInterrupt())
interrupted = true;
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -758,7 +540,6 @@
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 @@
if (r >= 0) {
setHeadAndPropagate(node, r);
p.next = null; // help GC
- failed = false;
return;
}
}
@@ -775,9 +555,9 @@
parkAndCheckInterrupt())
throw new InterruptedException();
}
- } finally {
- if (failed)
- cancelAcquire(node);
+ } catch (Throwable t) {
+ cancelAcquire(node);
+ throw t;
}
}
@@ -794,7 +574,6 @@
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 @@
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 @@
}
/**
- * Version of getFirstQueuedThread called when fastpath fails
+ * Version of getFirstQueuedThread called when fastpath fails.
*/
private Thread fullGetFirstQueuedThread() {
/*
@@ -1250,7 +1030,7 @@
*
* <p>An invocation of this method is equivalent to (but may be
* more efficient than):
- * <pre> {@code
+ * <pre> {@code
* getFirstQueuedThread() != Thread.currentThread() &&
* hasQueuedThreads()}</pre>
*
@@ -1270,7 +1050,7 @@
* tryAcquire} method for a fair, reentrant, exclusive mode
* synchronizer might look like this:
*
- * <pre> {@code
+ * <pre> {@code
* protected boolean tryAcquire(int arg) {
* if (isHeldExclusively()) {
* // A reentrant acquire; increment hold count
@@ -1306,8 +1086,7 @@
* 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 @@
* @return the collection of threads
*/
public final Collection<Thread> getQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
@@ -1350,7 +1129,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getExclusiveQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (!p.isShared()) {
Thread t = p.thread;
@@ -1370,7 +1149,7 @@
* @return the collection of threads
*/
public final Collection<Thread> getSharedQueuedThreads() {
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> list = new ArrayList<>();
for (Node p = tail; p != null; p = p.prev) {
if (p.isShared()) {
Thread t = p.thread;
@@ -1391,10 +1170,9 @@
* @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 @@
* @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 @@
/*
* 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 @@
*/
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 @@
* @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 @@
* @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 @@
* 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 @@
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 @@
/**
* Implements uninterruptible condition wait.
* <ol>
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
* </ol>
*/
public final void awaitUninterruptibly() {
@@ -1799,14 +1577,14 @@
/**
* Implements interruptible condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled or interrupted.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled or interrupted.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
public final void await() throws InterruptedException {
@@ -1831,30 +1609,33 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
* </ol>
*/
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 @@
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.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean awaitUntil(Date deadline)
@@ -1893,7 +1675,7 @@
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 @@
/**
* Implements timed condition wait.
* <ol>
- * <li> If current thread is interrupted, throw InterruptedException.
- * <li> Save lock state returned by {@link #getState}.
- * <li> Invoke {@link #release} with saved state as argument,
- * throwing IllegalMonitorStateException if it fails.
- * <li> Block until signalled, interrupted, or timed out.
- * <li> Reacquire by invoking specialized version of
- * {@link #acquire} with saved state as argument.
- * <li> If interrupted while blocked in step 4, throw InterruptedException.
- * <li> If timed out while blocked in step 4, return false, else true.
+ * <li>If current thread is interrupted, throw InterruptedException.
+ * <li>Save lock state returned by {@link #getState}.
+ * <li>Invoke {@link #release} with saved state as argument,
+ * throwing IllegalMonitorStateException if it fails.
+ * <li>Block until signalled, interrupted, or timed out.
+ * <li>Reacquire by invoking specialized version of
+ * {@link #acquire} with saved state as argument.
+ * <li>If interrupted while blocked in step 4, throw InterruptedException.
+ * <li>If timed out while blocked in step 4, return false, else true.
* </ol>
*/
public final boolean await(long time, TimeUnit unit)
@@ -1929,9 +1711,11 @@
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 @@
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 @@
protected final Collection<Thread> getWaitingThreads() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
- ArrayList<Thread> list = new ArrayList<Thread>();
+ ArrayList<Thread> 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 @@
* 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);
- }
-
- /**
- * CAS tail field. Used only by enq.
- */
- private final boolean compareAndSetTail(Node expect, Node update) {
- return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
+ private final void initializeSyncQueue() {
+ if (U.compareAndSwapObject(this, HEAD, null, new Node()))
+ tail = head;
}
/**
- * CAS waitStatus field of a node.
+ * CASes tail field.
*/
- 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);
+ private final boolean compareAndSetTail(Node expect, Node update) {
+ return U.compareAndSwapObject(this, TAIL, expect, update);
}
}