Skip to content

Commit 5a22015

Browse files
committed
[GR-31943] Set thread value before rethrowing
* Rethrowing the exception can itself throw an exception due to polling for safepoints.
1 parent 0194812 commit 5a22015

File tree

1 file changed

+8
-7
lines changed

1 file changed

+8
-7
lines changed

src/main/java/org/truffleruby/core/thread/ThreadManager.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -296,21 +296,23 @@ private void threadMain(RubyThread thread, Node currentNode, Supplier<Object> ta
296296
final Object result = task.get();
297297
setThreadValue(thread, result);
298298
// Handlers in the same order as in FiberManager
299+
// Each catch must either setThreadValue() (before rethrowing) or setException()
299300
} catch (KillException e) {
300301
setThreadValue(thread, Nil.INSTANCE);
301302
} catch (ThreadDeath e) { // Context#close(true)
303+
setThreadValue(thread, Nil.INSTANCE);
302304
throw e;
303305
} catch (RaiseException e) {
304306
setException(thread, e.getException(), currentNode);
305307
} catch (DynamicReturnException e) {
306308
setException(thread, context.getCoreExceptions().unexpectedReturn(currentNode), currentNode);
307309
} catch (ExitException e) {
308-
rethrowOnMainThread(currentNode, e);
309310
setThreadValue(thread, Nil.INSTANCE);
311+
rethrowOnMainThread(currentNode, e);
310312
} catch (Throwable e) {
311313
final RuntimeException runtimeException = printInternalError(e);
312-
rethrowOnMainThread(currentNode, runtimeException);
313314
setThreadValue(thread, Nil.INSTANCE);
315+
rethrowOnMainThread(currentNode, runtimeException);
314316
} finally {
315317
assert thread.value != null || thread.exception != null;
316318
cleanupKillOtherFibers(thread);
@@ -345,18 +347,18 @@ private void setThreadValue(RubyThread thread, Object value) {
345347
}
346348

347349
private void setException(RubyThread thread, RubyException exception, Node currentNode) {
348-
// A Thread is always shared (Thread.list)
349-
SharedObjects.propagate(language, thread, exception);
350-
351350
// We materialize the backtrace eagerly here, as the exception escapes the thread and needs
352351
// to capture the backtrace from this thread.
353352
final RaiseException truffleException = exception.backtrace.getRaiseException();
354353
if (truffleException != null) {
355354
TruffleStackTrace.fillIn(truffleException);
356355
}
357356

358-
final RubyThread mainThread = context.getThreadManager().getRootThread();
357+
// A Thread is always shared (Thread.list)
358+
SharedObjects.propagate(language, thread, exception);
359+
thread.exception = exception;
359360

361+
final RubyThread mainThread = context.getThreadManager().getRootThread();
360362
if (thread != mainThread) {
361363
final boolean isSystemExit = exception instanceof RubySystemExit;
362364

@@ -373,7 +375,6 @@ private void setException(RubyThread thread, RubyException exception, Node curre
373375
.raiseInThread(language, context, mainThread, exception, currentNode);
374376
}
375377
}
376-
thread.exception = exception;
377378
}
378379

379380
// Share the Ruby Thread before it can be accessed concurrently, and before it is added to Thread.list

0 commit comments

Comments
 (0)