8134855: Bulk integration of java.util.concurrent.locks classes
8051848: ReentrantReadWriteLock.ReadLock fails on unlock by different thread 8049843: Lack of save / restore interrupt mechanism undermines the StampedLock Reviewed-by: martin, psandoz, chegar
This commit is contained in:
parent
e82602a13a
commit
0f49a089d6
@ -34,11 +34,12 @@
|
||||
*/
|
||||
|
||||
package java.util.concurrent.locks;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import sun.misc.Unsafe;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.AbstractQueuedSynchronizer.Node;
|
||||
|
||||
/**
|
||||
* A version of {@link AbstractQueuedSynchronizer} in
|
||||
@ -76,221 +77,6 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
*/
|
||||
protected AbstractQueuedLongSynchronizer() { }
|
||||
|
||||
/**
|
||||
* Wait queue node class.
|
||||
*
|
||||
* <p>The wait queue is a variant of a "CLH" (Craig, Landin, and
|
||||
* Hagersten) lock queue. CLH locks are normally used for
|
||||
* spinlocks. We instead use them for blocking synchronizers, but
|
||||
* use the same basic tactic of holding some of the control
|
||||
* information about a thread in the predecessor of its node. A
|
||||
* "status" field in each node keeps track of whether a thread
|
||||
* should block. A node is signalled when its predecessor
|
||||
* releases. Each node of the queue otherwise serves as a
|
||||
* specific-notification-style monitor holding a single waiting
|
||||
* thread. The status field does NOT control whether threads are
|
||||
* granted locks etc though. A thread may try to acquire if it is
|
||||
* first in the queue. But being first does not guarantee success;
|
||||
* it only gives the right to contend. So the currently released
|
||||
* contender thread may need to rewait.
|
||||
*
|
||||
* <p>To enqueue into a CLH lock, you atomically splice it in as new
|
||||
* tail. To dequeue, you just set the head field.
|
||||
* <pre>
|
||||
* +------+ prev +-----+ +-----+
|
||||
* head | | <---- | | <---- | | tail
|
||||
* +------+ +-----+ +-----+
|
||||
* </pre>
|
||||
*
|
||||
* <p>Insertion into a CLH queue requires only a single atomic
|
||||
* operation on "tail", so there is a simple atomic point of
|
||||
* demarcation from unqueued to queued. Similarly, dequeuing
|
||||
* involves only updating the "head". However, it takes a bit
|
||||
* more work for nodes to determine who their successors are,
|
||||
* in part to deal with possible cancellation due to timeouts
|
||||
* and interrupts.
|
||||
*
|
||||
* <p>The "prev" links (not used in original CLH locks), are mainly
|
||||
* needed to handle cancellation. If a node is cancelled, its
|
||||
* successor is (normally) relinked to a non-cancelled
|
||||
* predecessor. For explanation of similar mechanics in the case
|
||||
* of spin locks, see the papers by Scott and Scherer at
|
||||
* http://www.cs.rochester.edu/u/scott/synchronization/
|
||||
*
|
||||
* <p>We also use "next" links to implement blocking mechanics.
|
||||
* The thread id for each node is kept in its own node, so a
|
||||
* predecessor signals the next node to wake up by traversing
|
||||
* next link to determine which thread it is. Determination of
|
||||
* successor must avoid races with newly queued nodes to set
|
||||
* the "next" fields of their predecessors. This is solved
|
||||
* when necessary by checking backwards from the atomically
|
||||
* updated "tail" when a node's successor appears to be null.
|
||||
* (Or, said differently, the next-links are an optimization
|
||||
* so that we don't usually need a backward scan.)
|
||||
*
|
||||
* <p>Cancellation introduces some conservatism to the basic
|
||||
* algorithms. Since we must poll for cancellation of other
|
||||
* nodes, we can miss noticing whether a cancelled node is
|
||||
* ahead or behind us. This is dealt with by always unparking
|
||||
* successors upon cancellation, allowing them to stabilize on
|
||||
* a new predecessor, unless we can identify an uncancelled
|
||||
* predecessor who will carry this responsibility.
|
||||
*
|
||||
* <p>CLH queues need a dummy header node to get started. But
|
||||
* we don't create them on construction, because it would be wasted
|
||||
* effort if there is never contention. Instead, the node
|
||||
* is constructed and head and tail pointers are set upon first
|
||||
* contention.
|
||||
*
|
||||
* <p>Threads waiting on Conditions use the same nodes, but
|
||||
* use an additional link. Conditions only need to link nodes
|
||||
* in simple (non-concurrent) linked queues because they are
|
||||
* only accessed when exclusively held. Upon await, a node is
|
||||
* inserted into a condition queue. Upon signal, the node is
|
||||
* transferred to the main queue. A special value of status
|
||||
* field is used to mark which queue a node is on.
|
||||
*
|
||||
* <p>Thanks go to Dave Dice, Mark Moir, Victor Luchangco, Bill
|
||||
* Scherer and Michael Scott, along with members of JSR-166
|
||||
* expert group, for helpful ideas, discussions, and critiques
|
||||
* on the design of this class.
|
||||
*/
|
||||
static final class Node {
|
||||
/** Marker to indicate a node is waiting in shared mode */
|
||||
static final Node SHARED = new Node();
|
||||
/** Marker to indicate a node is waiting in exclusive mode */
|
||||
static final Node EXCLUSIVE = null;
|
||||
|
||||
/** waitStatus value to indicate thread has cancelled */
|
||||
static final int CANCELLED = 1;
|
||||
/** waitStatus value to indicate successor's thread needs unparking */
|
||||
static final int SIGNAL = -1;
|
||||
/** waitStatus value to indicate thread is waiting on condition */
|
||||
static final int CONDITION = -2;
|
||||
/**
|
||||
* waitStatus value to indicate the next acquireShared should
|
||||
* unconditionally propagate
|
||||
*/
|
||||
static final int PROPAGATE = -3;
|
||||
|
||||
/**
|
||||
* Status field, taking on only the values:
|
||||
* SIGNAL: The successor of this node is (or will soon be)
|
||||
* blocked (via park), so the current node must
|
||||
* unpark its successor when it releases or
|
||||
* cancels. To avoid races, acquire methods must
|
||||
* first indicate they need a signal,
|
||||
* then retry the atomic acquire, and then,
|
||||
* on failure, block.
|
||||
* CANCELLED: This node is cancelled due to timeout or interrupt.
|
||||
* Nodes never leave this state. In particular,
|
||||
* a thread with cancelled node never again blocks.
|
||||
* CONDITION: This node is currently on a condition queue.
|
||||
* It will not be used as a sync queue node
|
||||
* until transferred, at which time the status
|
||||
* will be set to 0. (Use of this value here has
|
||||
* nothing to do with the other uses of the
|
||||
* field, but simplifies mechanics.)
|
||||
* PROPAGATE: A releaseShared should be propagated to other
|
||||
* nodes. This is set (for head node only) in
|
||||
* doReleaseShared to ensure propagation
|
||||
* continues, even if other operations have
|
||||
* since intervened.
|
||||
* 0: None of the above
|
||||
*
|
||||
* The values are arranged numerically to simplify use.
|
||||
* Non-negative values mean that a node doesn't need to
|
||||
* signal. So, most code doesn't need to check for particular
|
||||
* values, just for sign.
|
||||
*
|
||||
* The field is initialized to 0 for normal sync nodes, and
|
||||
* CONDITION for condition nodes. It is modified using CAS
|
||||
* (or when possible, unconditional volatile writes).
|
||||
*/
|
||||
volatile int waitStatus;
|
||||
|
||||
/**
|
||||
* Link to predecessor node that current node/thread relies on
|
||||
* for checking waitStatus. Assigned during enqueuing, and nulled
|
||||
* out (for sake of GC) only upon dequeuing. Also, upon
|
||||
* cancellation of a predecessor, we short-circuit while
|
||||
* finding a non-cancelled one, which will always exist
|
||||
* because the head node is never cancelled: A node becomes
|
||||
* head only as a result of successful acquire. A
|
||||
* cancelled thread never succeeds in acquiring, and a thread only
|
||||
* cancels itself, not any other node.
|
||||
*/
|
||||
volatile Node prev;
|
||||
|
||||
/**
|
||||
* Link to the successor node that the current node/thread
|
||||
* unparks upon release. Assigned during enqueuing, adjusted
|
||||
* when bypassing cancelled predecessors, and nulled out (for
|
||||
* sake of GC) when dequeued. The enq operation does not
|
||||
* assign next field of a predecessor until after attachment,
|
||||
* so seeing a null next field does not necessarily mean that
|
||||
* node is at end of queue. However, if a next field appears
|
||||
* to be null, we can scan prev's from the tail to
|
||||
* double-check. The next field of cancelled nodes is set to
|
||||
* point to the node itself instead of null, to make life
|
||||
* easier for isOnSyncQueue.
|
||||
*/
|
||||
volatile Node next;
|
||||
|
||||
/**
|
||||
* The thread that enqueued this node. Initialized on
|
||||
* construction and nulled out after use.
|
||||
*/
|
||||
volatile Thread thread;
|
||||
|
||||
/**
|
||||
* Link to next node waiting on condition, or the special
|
||||
* value SHARED. Because condition queues are accessed only
|
||||
* when holding in exclusive mode, we just need a simple
|
||||
* linked queue to hold nodes while they are waiting on
|
||||
* conditions. They are then transferred to the queue to
|
||||
* re-acquire. And because conditions can only be exclusive,
|
||||
* we save a field by using special value to indicate shared
|
||||
* mode.
|
||||
*/
|
||||
Node nextWaiter;
|
||||
|
||||
/**
|
||||
* Returns true if node is waiting in shared mode.
|
||||
*/
|
||||
final boolean isShared() {
|
||||
return nextWaiter == SHARED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns previous node, or throws NullPointerException if null.
|
||||
* Use when predecessor cannot be null. The null check could
|
||||
* be elided, but is present to help the VM.
|
||||
*
|
||||
* @return the predecessor of this node
|
||||
*/
|
||||
final Node predecessor() throws NullPointerException {
|
||||
Node p = prev;
|
||||
if (p == null)
|
||||
throw new NullPointerException();
|
||||
else
|
||||
return p;
|
||||
}
|
||||
|
||||
Node() { // Used to establish initial head or SHARED marker
|
||||
}
|
||||
|
||||
Node(Thread thread, Node mode) { // Used by addWaiter
|
||||
this.nextWaiter = mode;
|
||||
this.thread = thread;
|
||||
}
|
||||
|
||||
Node(Thread thread, int waitStatus) { // Used by Condition
|
||||
this.waitStatus = waitStatus;
|
||||
this.thread = thread;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Head of the wait queue, lazily initialized. Except for
|
||||
* initialization, it is modified only via method setHead. Note:
|
||||
@ -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
|
||||
*
|
||||
* <p>An invocation of this method is equivalent to (but may be
|
||||
* more efficient than):
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* getFirstQueuedThread() != Thread.currentThread() &&
|
||||
* hasQueuedThreads()}</pre>
|
||||
*
|
||||
@ -1270,7 +1050,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* tryAcquire} method for a fair, reentrant, exclusive mode
|
||||
* synchronizer might look like this:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* protected boolean tryAcquire(int arg) {
|
||||
* if (isHeldExclusively()) {
|
||||
* // A reentrant acquire; increment hold count
|
||||
@ -1306,8 +1086,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* acquire. The value is only an estimate because the number of
|
||||
* threads may change dynamically while this method traverses
|
||||
* internal data structures. This method is designed for use in
|
||||
* monitoring system state, not for synchronization
|
||||
* control.
|
||||
* monitoring system state, not for synchronization control.
|
||||
*
|
||||
* @return the estimated number of threads waiting to acquire
|
||||
*/
|
||||
@ -1332,7 +1111,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
Thread t = p.thread;
|
||||
if (t != null)
|
||||
@ -1350,7 +1129,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getExclusiveQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
if (!p.isShared()) {
|
||||
Thread t = p.thread;
|
||||
@ -1370,7 +1149,7 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getSharedQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
if (p.isShared()) {
|
||||
Thread t = p.thread;
|
||||
@ -1391,10 +1170,9 @@ 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.
|
||||
* <ol>
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* </ol>
|
||||
*/
|
||||
public final void awaitUninterruptibly() {
|
||||
@ -1799,14 +1577,14 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
/**
|
||||
* Implements interruptible condition wait.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled or interrupted.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled or interrupted.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* </ol>
|
||||
*/
|
||||
public final void await() throws InterruptedException {
|
||||
@ -1831,30 +1609,33 @@ public abstract class AbstractQueuedLongSynchronizer
|
||||
/**
|
||||
* Implements timed condition wait.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* </ol>
|
||||
*/
|
||||
public final long awaitNanos(long nanosTimeout)
|
||||
throws InterruptedException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
// We don't check for nanosTimeout <= 0L here, to allow
|
||||
// awaitNanos(0) as a way to "yield the lock".
|
||||
final long deadline = System.nanoTime() + nanosTimeout;
|
||||
long initialNanos = nanosTimeout;
|
||||
Node node = addConditionWaiter();
|
||||
long savedState = fullyRelease(node);
|
||||
final long deadline = System.nanoTime() + nanosTimeout;
|
||||
int interruptMode = 0;
|
||||
while (!isOnSyncQueue(node)) {
|
||||
if (nanosTimeout <= 0L) {
|
||||
transferAfterCancelledWait(node);
|
||||
break;
|
||||
}
|
||||
if (nanosTimeout >= spinForTimeoutThreshold)
|
||||
if (nanosTimeout >= SPIN_FOR_TIMEOUT_THRESHOLD)
|
||||
LockSupport.parkNanos(this, nanosTimeout);
|
||||
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
|
||||
break;
|
||||
@ -1866,21 +1647,22 @@ 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.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li> If timed out while blocked in step 4, return false, else true.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If timed out while blocked in step 4, return false, else true.
|
||||
* </ol>
|
||||
*/
|
||||
public final boolean awaitUntil(Date deadline)
|
||||
@ -1893,7 +1675,7 @@ 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.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li> If timed out while blocked in step 4, return false, else true.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If timed out while blocked in step 4, return false, else true.
|
||||
* </ol>
|
||||
*/
|
||||
public final boolean await(long time, TimeUnit unit)
|
||||
@ -1929,9 +1711,11 @@ 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<Thread> getWaitingThreads() {
|
||||
if (!isHeldExclusively())
|
||||
throw new IllegalMonitorStateException();
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
|
||||
if (w.waitStatus == Node.CONDITION) {
|
||||
Thread t = w.thread;
|
||||
@ -2037,59 +1821,40 @@ 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);
|
||||
}
|
||||
}
|
||||
|
@ -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}:
|
||||
*
|
||||
* <ul>
|
||||
* <li> {@link #tryAcquire}
|
||||
* <li> {@link #tryRelease}
|
||||
* <li> {@link #tryAcquireShared}
|
||||
* <li> {@link #tryReleaseShared}
|
||||
* <li> {@link #isHeldExclusively}
|
||||
* <li>{@link #tryAcquire}
|
||||
* <li>{@link #tryRelease}
|
||||
* <li>{@link #tryAcquireShared}
|
||||
* <li>{@link #tryReleaseShared}
|
||||
* <li>{@link #isHeldExclusively}
|
||||
* </ul>
|
||||
*
|
||||
* 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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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.
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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
|
||||
*
|
||||
* <p>An invocation of this method is equivalent to (but may be
|
||||
* more efficient than):
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* getFirstQueuedThread() != Thread.currentThread() &&
|
||||
* hasQueuedThreads()}</pre>
|
||||
*
|
||||
@ -1492,7 +1516,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* tryAcquire} method for a fair, reentrant, exclusive mode
|
||||
* synchronizer might look like this:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* protected boolean tryAcquire(int arg) {
|
||||
* if (isHeldExclusively()) {
|
||||
* // A reentrant acquire; increment hold count
|
||||
@ -1528,8 +1552,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* acquire. The value is only an estimate because the number of
|
||||
* threads may change dynamically while this method traverses
|
||||
* internal data structures. This method is designed for use in
|
||||
* monitoring system state, not for synchronization
|
||||
* control.
|
||||
* monitoring system state, not for synchronization control.
|
||||
*
|
||||
* @return the estimated number of threads waiting to acquire
|
||||
*/
|
||||
@ -1554,7 +1577,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
Thread t = p.thread;
|
||||
if (t != null)
|
||||
@ -1572,7 +1595,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getExclusiveQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
if (!p.isShared()) {
|
||||
Thread t = p.thread;
|
||||
@ -1592,7 +1615,7 @@ public abstract class AbstractQueuedSynchronizer
|
||||
* @return the collection of threads
|
||||
*/
|
||||
public final Collection<Thread> getSharedQueuedThreads() {
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node p = tail; p != null; p = p.prev) {
|
||||
if (p.isShared()) {
|
||||
Thread t = p.thread;
|
||||
@ -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.
|
||||
* <ol>
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* </ol>
|
||||
*/
|
||||
public final void awaitUninterruptibly() {
|
||||
@ -2019,14 +2041,14 @@ public abstract class AbstractQueuedSynchronizer
|
||||
/**
|
||||
* Implements interruptible condition wait.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled or interrupted.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled or interrupted.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* </ol>
|
||||
*/
|
||||
public final void await() throws InterruptedException {
|
||||
@ -2051,30 +2073,33 @@ public abstract class AbstractQueuedSynchronizer
|
||||
/**
|
||||
* Implements timed condition wait.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* </ol>
|
||||
*/
|
||||
public final long awaitNanos(long nanosTimeout)
|
||||
throws InterruptedException {
|
||||
if (Thread.interrupted())
|
||||
throw new InterruptedException();
|
||||
// We don't check for nanosTimeout <= 0L here, to allow
|
||||
// awaitNanos(0) as a way to "yield the lock".
|
||||
final long deadline = System.nanoTime() + nanosTimeout;
|
||||
long initialNanos = nanosTimeout;
|
||||
Node node = addConditionWaiter();
|
||||
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.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li> If timed out while blocked in step 4, return false, else true.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If timed out while blocked in step 4, return false, else true.
|
||||
* </ol>
|
||||
*/
|
||||
public final boolean awaitUntil(Date deadline)
|
||||
@ -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.
|
||||
* <ol>
|
||||
* <li> If current thread is interrupted, throw InterruptedException.
|
||||
* <li> Save lock state returned by {@link #getState}.
|
||||
* <li> Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li> Block until signalled, interrupted, or timed out.
|
||||
* <li> Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li> If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li> If timed out while blocked in step 4, return false, else true.
|
||||
* <li>If current thread is interrupted, throw InterruptedException.
|
||||
* <li>Save lock state returned by {@link #getState}.
|
||||
* <li>Invoke {@link #release} with saved state as argument,
|
||||
* throwing IllegalMonitorStateException if it fails.
|
||||
* <li>Block until signalled, interrupted, or timed out.
|
||||
* <li>Reacquire by invoking specialized version of
|
||||
* {@link #acquire} with saved state as argument.
|
||||
* <li>If interrupted while blocked in step 4, throw InterruptedException.
|
||||
* <li>If timed out while blocked in step 4, return false, else true.
|
||||
* </ol>
|
||||
*/
|
||||
public final boolean await(long time, TimeUnit unit)
|
||||
@ -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<Thread> getWaitingThreads() {
|
||||
if (!isHeldExclusively())
|
||||
throw new IllegalMonitorStateException();
|
||||
ArrayList<Thread> list = new ArrayList<Thread>();
|
||||
ArrayList<Thread> list = new ArrayList<>();
|
||||
for (Node w = firstWaiter; w != null; w = w.nextWaiter) {
|
||||
if (w.waitStatus == Node.CONDITION) {
|
||||
Thread t = w.thread;
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
* <p>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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:
|
||||
* <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
|
||||
* <pre> {@code awaitNanos(unit.toNanos(time)) > 0}</pre>
|
||||
*
|
||||
* @param time the maximum time to wait
|
||||
* @param unit the time unit of the {@code time} argument
|
||||
@ -410,7 +411,7 @@ public interface Condition {
|
||||
*
|
||||
* <p>The return value indicates whether the deadline has elapsed,
|
||||
* which can be used as follows:
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* boolean aMethod(Date deadline) {
|
||||
* boolean stillWaiting = true;
|
||||
* lock.lock();
|
||||
|
@ -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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* Lock l = ...;
|
||||
* l.lock();
|
||||
* try {
|
||||
@ -121,8 +122,9 @@ import java.util.concurrent.TimeUnit;
|
||||
* <p>All {@code Lock} implementations <em>must</em> enforce the same
|
||||
* memory synchronization semantics as provided by the built-in monitor
|
||||
* lock, as described in
|
||||
* <a href="http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4">
|
||||
* The Java Language Specification (17.4 Memory Model)</a>:
|
||||
* <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.4">
|
||||
* Chapter 17 of
|
||||
* <cite>The Java™ Language Specification</cite></a>:
|
||||
* <ul>
|
||||
* <li>A successful {@code lock} operation has the same memory
|
||||
* synchronization effects as a successful <em>Lock</em> action.
|
||||
@ -240,7 +242,7 @@ public interface Lock {
|
||||
* immediately with the value {@code false}.
|
||||
*
|
||||
* <p>A typical usage idiom for this method would be:
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* Lock lock = ...;
|
||||
* if (lock.tryLock()) {
|
||||
* try {
|
||||
|
@ -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 <em>may</em> 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.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* while (!canProceed()) { ... LockSupport.park(this); }}</pre>
|
||||
*
|
||||
* where neither {@code canProceed} nor any other actions prior to the
|
||||
@ -87,11 +90,11 @@ import sun.misc.Unsafe;
|
||||
*
|
||||
* <p><b>Sample Usage.</b> Here is a sketch of a first-in-first-out
|
||||
* non-reentrant lock class:
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* class FIFOMutex {
|
||||
* private final AtomicBoolean locked = new AtomicBoolean(false);
|
||||
* private final Queue<Thread> waiters
|
||||
* = new ConcurrentLinkedQueue<Thread>();
|
||||
* = 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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
*
|
||||
* <p>All {@code ReadWriteLock} implementations must guarantee that
|
||||
* the memory synchronization effects of {@code writeLock} operations
|
||||
|
@ -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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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.
|
||||
*
|
||||
* <li> Waiting threads are signalled in FIFO order.
|
||||
* <li>Waiting threads are signalled in FIFO order.
|
||||
*
|
||||
* <li>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* class X {
|
||||
* ReentrantLock lock = new ReentrantLock();
|
||||
* // ...
|
||||
@ -555,7 +556,7 @@ public class ReentrantLock implements Lock, java.io.Serializable {
|
||||
* <p>It can also be used to ensure that a reentrant lock is used
|
||||
* in a non-reentrant manner, for example:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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
|
||||
*/
|
||||
|
@ -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;
|
||||
*
|
||||
* <dl>
|
||||
* <dt><b><i>Non-fair mode (default)</i></b>
|
||||
* <dd>When constructed as non-fair (the default), the order of entry
|
||||
* <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
|
||||
* 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.
|
||||
*
|
||||
* <dt><b><i>Fair mode</i></b>
|
||||
* <dd>When constructed as fair, threads contend for entry using an
|
||||
* <dd style="font-family:'DejaVu Sans', Arial, Helvetica, sans-serif">
|
||||
* 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.
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* class RWDictionary {
|
||||
* private final Map<String, Data> m = new TreeMap<String, Data>();
|
||||
* private final Map<String, Data> 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
|
||||
* <p>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@code
|
||||
* if (lock.tryLock() ||
|
||||
* lock.tryLock(timeout, unit)) {
|
||||
* ...
|
||||
@ -874,7 +877,12 @@ public class ReentrantReadWriteLock
|
||||
* Attempts to release this lock.
|
||||
*
|
||||
* <p>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:
|
||||
*
|
||||
* <pre> {@code
|
||||
* <pre> {@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.
|
||||
*
|
||||
* <li> Waiting threads are signalled in FIFO order.
|
||||
* <li>Waiting threads are signalled in FIFO order.
|
||||
*
|
||||
* <li>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);
|
||||
}
|
||||
}
|
||||
|
@ -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. </li>
|
||||
* and all optimistic read validations will fail.
|
||||
*
|
||||
* <li><b>Reading.</b> 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. </li>
|
||||
* and timed versions of {@code tryReadLock} are also provided.
|
||||
*
|
||||
* <li><b>Optimistic Reading.</b> 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. </li>
|
||||
* methods.
|
||||
*
|
||||
* </ul>
|
||||
*
|
||||
@ -132,7 +128,7 @@ import java.util.concurrent.locks.LockSupport;
|
||||
* not strictly needed here because no exceptions can occur in their
|
||||
* bodies.<br>
|
||||
*
|
||||
* <pre>{@code
|
||||
* <pre> {@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);
|
||||
}
|
||||
}
|
||||
|
412
jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java
Normal file
412
jdk/test/java/util/concurrent/locks/Lock/CheckedLockLoops.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
129
jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java
Normal file
129
jdk/test/java/util/concurrent/locks/Lock/LoopHelpers.java
Normal file
@ -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();
|
||||
}
|
||||
|
||||
}
|
82
jdk/test/java/util/concurrent/locks/Lock/Mutex.java
Normal file
82
jdk/test/java/util/concurrent/locks/Lock/Mutex.java
Normal file
@ -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(); }
|
||||
}
|
@ -28,14 +28,32 @@
|
||||
* @author Martin Buchholz
|
||||
*/
|
||||
|
||||
// Note: this file is now out of sync with the jsr166 CVS repository due to the fix for 7092140
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
import java.io.*;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.io.Reader;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class TimedAcquireLeak {
|
||||
static String javahome() {
|
||||
@ -96,18 +114,34 @@ public class TimedAcquireLeak {
|
||||
Callable<T> callable) throws Throwable {
|
||||
p.getInputStream().read();
|
||||
T result = callable.call();
|
||||
OutputStream os = p.getOutputStream();
|
||||
os.write((byte)'\n'); os.flush();
|
||||
sendByte(p.getOutputStream());
|
||||
return result;
|
||||
}
|
||||
|
||||
/** No guarantees, but effective in practice. */
|
||||
private static void forceFullGc() {
|
||||
CountDownLatch finalizeDone = new CountDownLatch(1);
|
||||
WeakReference<?> ref = new WeakReference<Object>(new Object() {
|
||||
protected void finalize() { finalizeDone.countDown(); }});
|
||||
try {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
System.gc();
|
||||
if (finalizeDone.await(1L, SECONDS) && ref.get() == null) {
|
||||
System.runFinalization(); // try to pick up stragglers
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException unexpected) {
|
||||
throw new AssertionError("unexpected InterruptedException");
|
||||
}
|
||||
throw new AssertionError("failed to do a \"full\" gc");
|
||||
}
|
||||
|
||||
// To be called exactly twice by the child process
|
||||
public static void rendezvousChild() {
|
||||
try {
|
||||
for (int i = 0; i < 100; i++) {
|
||||
System.gc(); System.runFinalization(); Thread.sleep(50);
|
||||
}
|
||||
System.out.write((byte)'\n'); System.out.flush();
|
||||
forceFullGc();
|
||||
sendByte(System.out);
|
||||
System.in.read();
|
||||
} catch (Throwable t) { throw new Error(t); }
|
||||
}
|
||||
@ -118,6 +152,12 @@ public class TimedAcquireLeak {
|
||||
return matcher.group(group);
|
||||
}
|
||||
|
||||
/** It's all about sending a message! */
|
||||
static void sendByte(OutputStream s) throws IOException {
|
||||
s.write('!');
|
||||
s.flush();
|
||||
}
|
||||
|
||||
static int objectsInUse(final Process child,
|
||||
final String childPid,
|
||||
final String className) {
|
||||
@ -155,6 +195,9 @@ public class TimedAcquireLeak {
|
||||
childClassName, uniqueID
|
||||
};
|
||||
final Process p = new ProcessBuilder(jobCmd).start();
|
||||
// Ensure subprocess jvm has started, so that jps can find it
|
||||
p.getInputStream().read();
|
||||
sendByte(p.getOutputStream());
|
||||
|
||||
final String childPid =
|
||||
match(commandOutputOf(jps, "-m"),
|
||||
@ -167,10 +210,19 @@ public class TimedAcquireLeak {
|
||||
failed += p.exitValue();
|
||||
|
||||
// Check that no objects were leaked.
|
||||
//
|
||||
// TODO: This test is very brittle, depending on current JDK
|
||||
// implementation, and needing occasional adjustment.
|
||||
System.out.printf("%d -> %d%n", n0, n1);
|
||||
check(Math.abs(n1 - n0) < 2); // Almost always n0 == n1
|
||||
check(n1 < 20);
|
||||
// Almost always n0 == n1
|
||||
// Maximum jitter observed in practice is 10 -> 17
|
||||
check(Math.abs(n1 - n0) < 10);
|
||||
check(n1 < 25);
|
||||
drainers.shutdown();
|
||||
if (!drainers.awaitTermination(10L, SECONDS)) {
|
||||
drainers.shutdownNow(); // last resort
|
||||
throw new AssertionError("thread pool did not terminate");
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------
|
||||
@ -187,6 +239,10 @@ public class TimedAcquireLeak {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
// Synchronize with parent process, so that jps can find us
|
||||
sendByte(System.out);
|
||||
System.in.read();
|
||||
|
||||
final ReentrantLock lock = new ReentrantLock();
|
||||
lock.lock();
|
||||
|
||||
|
163
jdk/test/java/util/concurrent/locks/LockSupport/ParkLoops.java
Normal file
163
jdk/test/java/util/concurrent/locks/LockSupport/ParkLoops.java
Normal file
@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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 Martin Buchholz 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 8074773
|
||||
* @summary Stress test looks for lost unparks
|
||||
* @run main/timeout=1200 ParkLoops
|
||||
*/
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.ThreadInfo;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.atomic.AtomicReferenceArray;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
|
||||
public final class ParkLoops {
|
||||
static final int THREADS = 4; // must be power of two
|
||||
// static final int ITERS = 2_000_000;
|
||||
// static final int TIMEOUT = 3500; // in seconds
|
||||
static final int ITERS = 100_000;
|
||||
static final int TIMEOUT = 1000; // in seconds
|
||||
|
||||
static class Parker implements Runnable {
|
||||
static {
|
||||
// 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;
|
||||
}
|
||||
|
||||
private final AtomicReferenceArray<Thread> threads;
|
||||
private final CountDownLatch done;
|
||||
|
||||
Parker(AtomicReferenceArray<Thread> threads, CountDownLatch done) {
|
||||
this.threads = threads;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final SimpleRandom rng = new SimpleRandom();
|
||||
final Thread current = Thread.currentThread();
|
||||
for (int k = ITERS, j; k > 0; k--) {
|
||||
do {
|
||||
j = rng.next() & (THREADS - 1);
|
||||
} while (!threads.compareAndSet(j, null, current));
|
||||
do { // handle spurious wakeups
|
||||
LockSupport.park();
|
||||
} while (threads.get(j) == current);
|
||||
}
|
||||
done.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
static class Unparker implements Runnable {
|
||||
static {
|
||||
// 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;
|
||||
}
|
||||
|
||||
private final AtomicReferenceArray<Thread> threads;
|
||||
private final CountDownLatch done;
|
||||
|
||||
Unparker(AtomicReferenceArray<Thread> threads, CountDownLatch done) {
|
||||
this.threads = threads;
|
||||
this.done = done;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
final SimpleRandom rng = new SimpleRandom();
|
||||
for (int n = 0; (n++ & 0xff) != 0 || done.getCount() > 0;) {
|
||||
int j = rng.next() & (THREADS - 1);
|
||||
Thread parker = threads.get(j);
|
||||
if (parker != null &&
|
||||
threads.compareAndSet(j, parker, null)) {
|
||||
LockSupport.unpark(parker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
final ExecutorService pool = Executors.newCachedThreadPool();
|
||||
final AtomicReferenceArray<Thread> threads
|
||||
= new AtomicReferenceArray<>(THREADS);
|
||||
final CountDownLatch done = new CountDownLatch(THREADS);
|
||||
final Runnable parker = new Parker(threads, done);
|
||||
final Runnable unparker = new Unparker(threads, done);
|
||||
for (int i = 0; i < THREADS; i++) {
|
||||
pool.submit(parker);
|
||||
pool.submit(unparker);
|
||||
}
|
||||
try {
|
||||
if (!done.await(TIMEOUT, SECONDS)) {
|
||||
dumpAllStacks();
|
||||
throw new AssertionError("lost unpark");
|
||||
}
|
||||
} finally {
|
||||
pool.shutdown();
|
||||
pool.awaitTermination(10L, SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
static void dumpAllStacks() {
|
||||
ThreadInfo[] threadInfos =
|
||||
ManagementFactory.getThreadMXBean().dumpAllThreads(true, true);
|
||||
for (ThreadInfo threadInfo : threadInfos) {
|
||||
System.err.print(threadInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 int next() {
|
||||
long nextseed = (seed * multiplier + addend) & mask;
|
||||
seed = nextseed;
|
||||
return ((int)(nextseed >>> 17)) & 0x7FFFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
@ -35,10 +35,10 @@
|
||||
* @test
|
||||
* @bug 4486658
|
||||
* @run main/timeout=2800 CancelledLockLoops
|
||||
* @summary tests lockInterruptibly.
|
||||
* Checks for responsiveness of locks to interrupts. Runs under that
|
||||
* assumption that ITERS_VALUE computations require more than TIMEOUT
|
||||
* msecs to complete.
|
||||
* @summary tests ReentrantLock.lockInterruptibly.
|
||||
* Checks for responsiveness of locks to interrupts. Runs under the
|
||||
* assumption that ITERS computations require more than TIMEOUT msecs
|
||||
* to complete.
|
||||
*/
|
||||
|
||||
import java.util.concurrent.*;
|
||||
@ -48,11 +48,12 @@ import java.util.*;
|
||||
public final class CancelledLockLoops {
|
||||
static final Random rng = new Random();
|
||||
static boolean print = false;
|
||||
static final int ITERS = 5000000;
|
||||
static final int ITERS = 1000000;
|
||||
static final long TIMEOUT = 100;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
int maxThreads = (args.length > 0) ? Integer.parseInt(args[0]) : 5;
|
||||
|
||||
print = true;
|
||||
|
||||
for (int i = 2; i <= maxThreads; i += (i+1) >>> 1) {
|
||||
@ -112,7 +113,7 @@ public final class CancelledLockLoops {
|
||||
lock.unlock();
|
||||
}
|
||||
if (c != 2)
|
||||
throw new Error("Completed != 2");
|
||||
throw new Error("Completed == " + c + "; expected 2");
|
||||
int r = result;
|
||||
if (r == 0) // avoid overoptimization
|
||||
System.out.println("useless result: " + r);
|
||||
|
@ -57,7 +57,6 @@ public final class TimeoutLockLoops {
|
||||
if (args.length > 0)
|
||||
maxThreads = Integer.parseInt(args[0]);
|
||||
|
||||
|
||||
print = true;
|
||||
|
||||
for (int i = 1; i <= maxThreads; i += (i+1) >>> 1) {
|
||||
@ -140,6 +139,4 @@ public final class TimeoutLockLoops {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ public class MapLoops {
|
||||
int position;
|
||||
int total;
|
||||
|
||||
Runner(Map<Integer,Integer> map, Integer[] key, CyclicBarrier barrier) {
|
||||
Runner(Map<Integer,Integer> map, Integer[] key, CyclicBarrier barrier) {
|
||||
this.map = map;
|
||||
this.key = key;
|
||||
this.barrier = barrier;
|
||||
@ -142,7 +142,7 @@ public class MapLoops {
|
||||
}
|
||||
|
||||
int step() {
|
||||
// random-walk around key positions, bunching accesses
|
||||
// random-walk around key positions, bunching accesses
|
||||
int r = rng.next();
|
||||
position += (r & 7) - 3;
|
||||
while (position >= key.length) position -= key.length;
|
||||
@ -156,7 +156,7 @@ public class MapLoops {
|
||||
throw new Error("bad mapping: " + x + " to " + k);
|
||||
|
||||
if (r < removesPerMaxRandom) {
|
||||
// get awy from this position
|
||||
// get away from this position
|
||||
position = r % key.length;
|
||||
map.remove(k);
|
||||
return 2;
|
||||
|
@ -35,13 +35,11 @@ import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.locks.*;
|
||||
|
||||
|
||||
/**
|
||||
* This is an incomplete implementation of a wrapper class
|
||||
* that places read-write locks around unsynchronized Maps.
|
||||
* Exists as a sample input for MapLoops test.
|
||||
*/
|
||||
|
||||
public class RWMap implements Map {
|
||||
private final Map m;
|
||||
private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
|
||||
@ -86,7 +84,6 @@ public class RWMap implements Map {
|
||||
finally { rwl.readLock().unlock(); }
|
||||
}
|
||||
|
||||
|
||||
public Set keySet() { // Not implemented
|
||||
return null;
|
||||
}
|
||||
|
@ -78,7 +78,7 @@ public class Basic {
|
||||
abstract static class Locker extends Thread {
|
||||
static AtomicInteger count = new AtomicInteger(1);
|
||||
private volatile Throwable thrown;
|
||||
private volatile long stamp;;
|
||||
private volatile long stamp;
|
||||
protected void thrown(Throwable thrown) { this.thrown = thrown; }
|
||||
public Throwable thrown() { return thrown; }
|
||||
protected void stamp(long stamp) { this.stamp = stamp; }
|
||||
@ -371,7 +371,7 @@ public class Basic {
|
||||
check(!sl.tryUnlockRead());
|
||||
check(!sl.tryUnlockWrite());
|
||||
check(sl.tryOptimisticRead() != 0L);
|
||||
Locker[] wThreads = new Locker[100];;
|
||||
Locker[] wThreads = new Locker[100];
|
||||
for (int j=0; j<100; j++)
|
||||
wThreads[j] = writers.next();
|
||||
for (int j=0; j<100; j++)
|
||||
@ -401,7 +401,7 @@ public class Basic {
|
||||
check(!sl.tryUnlockRead());
|
||||
check(!sl.tryUnlockWrite());
|
||||
check(sl.tryOptimisticRead() != 0L);
|
||||
Locker[] rThreads = new Locker[100];;
|
||||
Locker[] rThreads = new Locker[100];
|
||||
for (int j=0; j<100; j++)
|
||||
rThreads[j] = readers.next();
|
||||
for (int j=0; j<100; j++)
|
||||
|
Loading…
x
Reference in New Issue
Block a user