Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8066859: java/lang/ref/OOMEInReferenceHandler.java failed with java.lang.Exception: Reference Handler thread died #9427

Closed
wants to merge 11 commits into from
Expand Up @@ -195,31 +195,53 @@ private boolean casTail(Node c, Node v) {
return U.compareAndSetReference(this, TAIL, c, v);
}

/** tries once to CAS a new dummy node for head */
private void tryInitializeHead() {
Node h = new ExclusiveNode();
if (U.compareAndSetReference(this, HEAD, null, h))
tail = h;
/**
* Tries to CAS a new dummy node for head.
* Returns new tail, or null if OutOfMemory
*/
private Node tryInitializeHead() {
for (Node h = null, t;;) {
if ((t = tail) != null)
return t;
else if (head != null)
Thread.onSpinWait();
else {
if (h == null) {
try {
h = new ExclusiveNode();
} catch (OutOfMemoryError oome) {
return null;
}
}
if (U.compareAndSetReference(this, HEAD, null, h))
return tail = h;
}
}
}


/**
* Enqueues the node unless null. (Currently used only for
* ConditionNodes; other cases are interleaved with acquires.)
*/
final void enqueue(Node node) {
final void enqueue(ConditionNode node) {
if (node != null) {
for (;;) {
Node t = tail;
boolean unpark = false;
for (Node t;;) {
if ((t = tail) == null && (t = tryInitializeHead()) == null) {
unpark = true; // wake up to spin on OOME
break;
}
node.setPrevRelaxed(t); // avoid unnecessary fence
if (t == null) // initialize
tryInitializeHead();
else if (casTail(t, node)) {
if (casTail(t, node)) {
t.next = node;
if (t.status < 0) // wake up to clean link
LockSupport.unpark(node.waiter);
unpark = true;
break;
}
}
if (unpark)
LockSupport.unpark(node.waiter);
}
}

Expand Down Expand Up @@ -278,7 +300,10 @@ final int acquire(Node node, long arg, boolean shared,
* Check if node now first
* if so, ensure head stable, else ensure valid predecessor
* if node is first or not yet enqueued, try acquiring
* else if queue is not initialized, do so by attaching new header node
* resort to spinwait on OOME trying to create node
* else if node not yet created, create it
* resort to spinwait on OOME trying to create node
* else if not yet enqueued, try once to enqueue
* else if woken from park, retry (up to postSpins times)
* else if WAITING status not set, set and retry
Expand Down Expand Up @@ -321,18 +346,20 @@ final int acquire(Node node, long arg, boolean shared,
return 1;
}
}
if (node == null) { // allocate; retry before enqueue
if (shared)
node = new SharedNode();
else
node = new ExclusiveNode();
Node t;
if ((t = tail) == null) { // initialize queue
if (tryInitializeHead() == null)
return acquireOnOOME(shared, arg);
} else if (node == null) { // allocate; retry before enqueue
try {
node = (shared) ? new SharedNode() : new ExclusiveNode();
} catch (OutOfMemoryError oome) {
return acquireOnOOME(shared, arg);
}
} else if (pred == null) { // try to enqueue
node.waiter = current;
Node t = tail;
node.setPrevRelaxed(t); // avoid unnecessary fence
if (t == null)
tryInitializeHead();
else if (!casTail(t, node))
if (!casTail(t, node))
node.setPrevRelaxed(null); // back out
else
t.next = node;
Expand All @@ -358,9 +385,23 @@ else if ((nanos = time - System.nanoTime()) > 0L)
return cancelAcquire(node, interrupted, interruptible);
}

/**
* Spin-waits with backoff; used only upon OOME failures during acquire.
*/
private int acquireOnOOME(boolean shared, long arg) {
for (long nanos = 1L;;) {
if (shared ? (tryAcquireShared(arg) >= 0) : tryAcquire(arg))
return 1;
U.park(false, nanos); // must use Unsafe park to sleep
if (nanos < 1L << 30) // max about 1 second
nanos <<= 1;
}
}

/**
* Possibly repeatedly traverses from tail, unsplicing cancelled
* nodes until none are found.
* nodes until none are found. Unparks nodes that may have been
* relinked to be next eligible acquirer.
*/
private void cleanQueue() {
for (;;) { // restart point
Expand Down Expand Up @@ -1067,6 +1108,12 @@ public class ConditionObject implements Condition, java.io.Serializable {
/** Last node of condition queue. */
private transient ConditionNode lastWaiter;

/**
* Fixed delay in nanoseconds between releasing and reacquiring
* lock during Condition waits that encounter OutOfMemoryErrors
*/
static final long OOME_COND_WAIT_DELAY = 10L * 1000L * 1000L; // 10 ms

/**
* Creates a new {@code ConditionObject} instance.
*/
Expand Down Expand Up @@ -1103,7 +1150,7 @@ public final void signal() {
ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
if (first != null)
else if (first != null)
doSignal(first, false);
}

Expand All @@ -1118,7 +1165,7 @@ public final void signalAll() {
ConditionNode first = firstWaiter;
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
if (first != null)
else if (first != null)
doSignal(first, true);
}

Expand Down Expand Up @@ -1185,6 +1232,26 @@ private void unlinkCancelledWaiters(ConditionNode node) {
}
}

/**
* Constructs objects needed for condition wait. On OOME,
* releases lock, sleeps, reacquires, and returns null.
*/
private ConditionNode newConditionNode() {
long savedState;
if (tryInitializeHead() != null) {
try {
return new ConditionNode();
} catch (OutOfMemoryError oome) {
}
}
// fall through if encountered OutOfMemoryError
if (!isHeldExclusively() || !release(savedState = getState()))
throw new IllegalMonitorStateException();
Comment on lines +1247 to +1249
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I still don't see how an OOME can possibly indicate we might somehow be in a state where we need to throw IMSE!

U.park(false, OOME_COND_WAIT_DELAY);
acquireOnOOME(false, savedState);
return null;
}

/**
* Implements uninterruptible condition wait.
* <ol>
Expand All @@ -1197,7 +1264,9 @@ private void unlinkCancelledWaiters(ConditionNode node) {
* </ol>
*/
public final void awaitUninterruptibly() {
ConditionNode node = new ConditionNode();
ConditionNode node = newConditionNode();
if (node == null)
return;
long savedState = enableWait(node);
LockSupport.setCurrentBlocker(this); // for back-compatibility
boolean interrupted = false, rejected = false;
Expand Down Expand Up @@ -1241,7 +1310,9 @@ else if ((node.status & COND) != 0) {
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
ConditionNode node = new ConditionNode();
ConditionNode node = newConditionNode();
if (node == null)
return;
long savedState = enableWait(node);
LockSupport.setCurrentBlocker(this); // for back-compatibility
boolean interrupted = false, cancelled = false, rejected = false;
Expand Down Expand Up @@ -1292,7 +1363,9 @@ public final long awaitNanos(long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
ConditionNode node = new ConditionNode();
ConditionNode node = newConditionNode();
if (node == null)
return nanosTimeout - OOME_COND_WAIT_DELAY;
long savedState = enableWait(node);
long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
long deadline = System.nanoTime() + nanos;
Expand Down Expand Up @@ -1336,7 +1409,9 @@ public final boolean awaitUntil(Date deadline)
long abstime = deadline.getTime();
if (Thread.interrupted())
throw new InterruptedException();
ConditionNode node = new ConditionNode();
ConditionNode node = newConditionNode();
if (node == null)
return false;
long savedState = enableWait(node);
boolean cancelled = false, interrupted = false;
while (!canReacquire(node)) {
Expand Down Expand Up @@ -1377,7 +1452,9 @@ public final boolean await(long time, TimeUnit unit)
long nanosTimeout = unit.toNanos(time);
if (Thread.interrupted())
throw new InterruptedException();
ConditionNode node = new ConditionNode();
ConditionNode node = newConditionNode();
if (node == null)
return false;
long savedState = enableWait(node);
long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
long deadline = System.nanoTime() + nanos;
Expand Down