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 CollectiongetQueuedThreads() { - 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. * - *
*/ public final void awaitUninterruptibly() { @@ -1799,14 +1577,14 @@ public abstract class AbstractQueuedLongSynchronizer /** * Implements interruptible 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 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 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 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 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 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- 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. *
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}: * * - *
* * 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: * - *- {@link #tryAcquire} - *
- {@link #tryRelease} - *
- {@link #tryAcquireShared} - *
- {@link #tryReleaseShared} - *
- {@link #isHeldExclusively} + *
- {@link #tryAcquire} + *
- {@link #tryRelease} + *
- {@link #tryAcquireShared} + *
- {@link #tryReleaseShared} + *
- {@link #isHeldExclusively} *
{@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 CollectiongetQueuedThreads() { - 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. * - *
*/ public final void awaitUninterruptibly() { @@ -2019,14 +2041,14 @@ public abstract class AbstractQueuedSynchronizer /** * Implements interruptible 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 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 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 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 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 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- 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. *
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: *
*
* @@ -132,7 +128,7 @@ import java.util.concurrent.locks.LockSupport; * not strictly needed here because no exceptions can occur in their * bodies.- 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 Queuewaiters - * = 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 Mapm = 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. * *
* - *{@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 { Callablecallable) 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