Skip to content

Commit 5358045

Browse files
author
Doug Lea
committed
8066859: java/lang/ref/OOMEInReferenceHandler.java failed with java.lang.Exception: Reference Handler thread died
Reviewed-by: alanb
1 parent 2583feb commit 5358045

File tree

5 files changed

+351
-61
lines changed

5 files changed

+351
-61
lines changed

src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedLongSynchronizer.java

Lines changed: 106 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -195,31 +195,53 @@ private boolean casTail(Node c, Node v) {
195195
return U.compareAndSetReference(this, TAIL, c, v);
196196
}
197197

198-
/** tries once to CAS a new dummy node for head */
199-
private void tryInitializeHead() {
200-
Node h = new ExclusiveNode();
201-
if (U.compareAndSetReference(this, HEAD, null, h))
202-
tail = h;
198+
/**
199+
* Tries to CAS a new dummy node for head.
200+
* Returns new tail, or null if OutOfMemory
201+
*/
202+
private Node tryInitializeHead() {
203+
for (Node h = null, t;;) {
204+
if ((t = tail) != null)
205+
return t;
206+
else if (head != null)
207+
Thread.onSpinWait();
208+
else {
209+
if (h == null) {
210+
try {
211+
h = new ExclusiveNode();
212+
} catch (OutOfMemoryError oome) {
213+
return null;
214+
}
215+
}
216+
if (U.compareAndSetReference(this, HEAD, null, h))
217+
return tail = h;
218+
}
219+
}
203220
}
204221

222+
205223
/**
206224
* Enqueues the node unless null. (Currently used only for
207225
* ConditionNodes; other cases are interleaved with acquires.)
208226
*/
209-
final void enqueue(Node node) {
227+
final void enqueue(ConditionNode node) {
210228
if (node != null) {
211-
for (;;) {
212-
Node t = tail;
229+
boolean unpark = false;
230+
for (Node t;;) {
231+
if ((t = tail) == null && (t = tryInitializeHead()) == null) {
232+
unpark = true; // wake up to spin on OOME
233+
break;
234+
}
213235
node.setPrevRelaxed(t); // avoid unnecessary fence
214-
if (t == null) // initialize
215-
tryInitializeHead();
216-
else if (casTail(t, node)) {
236+
if (casTail(t, node)) {
217237
t.next = node;
218238
if (t.status < 0) // wake up to clean link
219-
LockSupport.unpark(node.waiter);
239+
unpark = true;
220240
break;
221241
}
222242
}
243+
if (unpark)
244+
LockSupport.unpark(node.waiter);
223245
}
224246
}
225247

@@ -278,7 +300,10 @@ final int acquire(Node node, long arg, boolean shared,
278300
* Check if node now first
279301
* if so, ensure head stable, else ensure valid predecessor
280302
* if node is first or not yet enqueued, try acquiring
303+
* else if queue is not initialized, do so by attaching new header node
304+
* resort to spinwait on OOME trying to create node
281305
* else if node not yet created, create it
306+
* resort to spinwait on OOME trying to create node
282307
* else if not yet enqueued, try once to enqueue
283308
* else if woken from park, retry (up to postSpins times)
284309
* else if WAITING status not set, set and retry
@@ -321,18 +346,20 @@ final int acquire(Node node, long arg, boolean shared,
321346
return 1;
322347
}
323348
}
324-
if (node == null) { // allocate; retry before enqueue
325-
if (shared)
326-
node = new SharedNode();
327-
else
328-
node = new ExclusiveNode();
349+
Node t;
350+
if ((t = tail) == null) { // initialize queue
351+
if (tryInitializeHead() == null)
352+
return acquireOnOOME(shared, arg);
353+
} else if (node == null) { // allocate; retry before enqueue
354+
try {
355+
node = (shared) ? new SharedNode() : new ExclusiveNode();
356+
} catch (OutOfMemoryError oome) {
357+
return acquireOnOOME(shared, arg);
358+
}
329359
} else if (pred == null) { // try to enqueue
330360
node.waiter = current;
331-
Node t = tail;
332361
node.setPrevRelaxed(t); // avoid unnecessary fence
333-
if (t == null)
334-
tryInitializeHead();
335-
else if (!casTail(t, node))
362+
if (!casTail(t, node))
336363
node.setPrevRelaxed(null); // back out
337364
else
338365
t.next = node;
@@ -358,9 +385,23 @@ else if ((nanos = time - System.nanoTime()) > 0L)
358385
return cancelAcquire(node, interrupted, interruptible);
359386
}
360387

388+
/**
389+
* Spin-waits with backoff; used only upon OOME failures during acquire.
390+
*/
391+
private int acquireOnOOME(boolean shared, long arg) {
392+
for (long nanos = 1L;;) {
393+
if (shared ? (tryAcquireShared(arg) >= 0) : tryAcquire(arg))
394+
return 1;
395+
U.park(false, nanos); // must use Unsafe park to sleep
396+
if (nanos < 1L << 30) // max about 1 second
397+
nanos <<= 1;
398+
}
399+
}
400+
361401
/**
362402
* Possibly repeatedly traverses from tail, unsplicing cancelled
363-
* nodes until none are found.
403+
* nodes until none are found. Unparks nodes that may have been
404+
* relinked to be next eligible acquirer.
364405
*/
365406
private void cleanQueue() {
366407
for (;;) { // restart point
@@ -1067,6 +1108,12 @@ public class ConditionObject implements Condition, java.io.Serializable {
10671108
/** Last node of condition queue. */
10681109
private transient ConditionNode lastWaiter;
10691110

1111+
/**
1112+
* Fixed delay in nanoseconds between releasing and reacquiring
1113+
* lock during Condition waits that encounter OutOfMemoryErrors
1114+
*/
1115+
static final long OOME_COND_WAIT_DELAY = 10L * 1000L * 1000L; // 10 ms
1116+
10701117
/**
10711118
* Creates a new {@code ConditionObject} instance.
10721119
*/
@@ -1103,7 +1150,7 @@ public final void signal() {
11031150
ConditionNode first = firstWaiter;
11041151
if (!isHeldExclusively())
11051152
throw new IllegalMonitorStateException();
1106-
if (first != null)
1153+
else if (first != null)
11071154
doSignal(first, false);
11081155
}
11091156

@@ -1118,7 +1165,7 @@ public final void signalAll() {
11181165
ConditionNode first = firstWaiter;
11191166
if (!isHeldExclusively())
11201167
throw new IllegalMonitorStateException();
1121-
if (first != null)
1168+
else if (first != null)
11221169
doSignal(first, true);
11231170
}
11241171

@@ -1185,6 +1232,26 @@ private void unlinkCancelledWaiters(ConditionNode node) {
11851232
}
11861233
}
11871234

1235+
/**
1236+
* Constructs objects needed for condition wait. On OOME,
1237+
* releases lock, sleeps, reacquires, and returns null.
1238+
*/
1239+
private ConditionNode newConditionNode() {
1240+
long savedState;
1241+
if (tryInitializeHead() != null) {
1242+
try {
1243+
return new ConditionNode();
1244+
} catch (OutOfMemoryError oome) {
1245+
}
1246+
}
1247+
// fall through if encountered OutOfMemoryError
1248+
if (!isHeldExclusively() || !release(savedState = getState()))
1249+
throw new IllegalMonitorStateException();
1250+
U.park(false, OOME_COND_WAIT_DELAY);
1251+
acquireOnOOME(false, savedState);
1252+
return null;
1253+
}
1254+
11881255
/**
11891256
* Implements uninterruptible condition wait.
11901257
* <ol>
@@ -1197,7 +1264,9 @@ private void unlinkCancelledWaiters(ConditionNode node) {
11971264
* </ol>
11981265
*/
11991266
public final void awaitUninterruptibly() {
1200-
ConditionNode node = new ConditionNode();
1267+
ConditionNode node = newConditionNode();
1268+
if (node == null)
1269+
return;
12011270
long savedState = enableWait(node);
12021271
LockSupport.setCurrentBlocker(this); // for back-compatibility
12031272
boolean interrupted = false, rejected = false;
@@ -1241,7 +1310,9 @@ else if ((node.status & COND) != 0) {
12411310
public final void await() throws InterruptedException {
12421311
if (Thread.interrupted())
12431312
throw new InterruptedException();
1244-
ConditionNode node = new ConditionNode();
1313+
ConditionNode node = newConditionNode();
1314+
if (node == null)
1315+
return;
12451316
long savedState = enableWait(node);
12461317
LockSupport.setCurrentBlocker(this); // for back-compatibility
12471318
boolean interrupted = false, cancelled = false, rejected = false;
@@ -1292,7 +1363,9 @@ public final long awaitNanos(long nanosTimeout)
12921363
throws InterruptedException {
12931364
if (Thread.interrupted())
12941365
throw new InterruptedException();
1295-
ConditionNode node = new ConditionNode();
1366+
ConditionNode node = newConditionNode();
1367+
if (node == null)
1368+
return nanosTimeout - OOME_COND_WAIT_DELAY;
12961369
long savedState = enableWait(node);
12971370
long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
12981371
long deadline = System.nanoTime() + nanos;
@@ -1336,7 +1409,9 @@ public final boolean awaitUntil(Date deadline)
13361409
long abstime = deadline.getTime();
13371410
if (Thread.interrupted())
13381411
throw new InterruptedException();
1339-
ConditionNode node = new ConditionNode();
1412+
ConditionNode node = newConditionNode();
1413+
if (node == null)
1414+
return false;
13401415
long savedState = enableWait(node);
13411416
boolean cancelled = false, interrupted = false;
13421417
while (!canReacquire(node)) {
@@ -1377,7 +1452,9 @@ public final boolean await(long time, TimeUnit unit)
13771452
long nanosTimeout = unit.toNanos(time);
13781453
if (Thread.interrupted())
13791454
throw new InterruptedException();
1380-
ConditionNode node = new ConditionNode();
1455+
ConditionNode node = newConditionNode();
1456+
if (node == null)
1457+
return false;
13811458
long savedState = enableWait(node);
13821459
long nanos = (nanosTimeout < 0L) ? 0L : nanosTimeout;
13831460
long deadline = System.nanoTime() + nanos;

0 commit comments

Comments
 (0)