Skip to content

Commit 23d0bee

Browse files
committed
Thread termination note in isolate tear-down
Introduced -R:TearDownWarningSeconds Depracated -R:TearDownWarningNanos and -R:TearDownFailureSeconds
1 parent 17fe52a commit 23d0bee

File tree

3 files changed

+73
-24
lines changed

3 files changed

+73
-24
lines changed

espresso/src/com.oracle.truffle.espresso.mokapot/include/graal_isolate_dynamic.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,14 @@ typedef int (*graal_detach_thread_fn_t)(graal_isolatethread_t* thread);
127127
* waiting for any attached threads to detach from it, then discards its objects,
128128
* threads, and any other state or context that is associated with it.
129129
* Returns 0 on success, or a non-zero value on failure.
130+
*
131+
* If this call blocks indefinitely, this means there are still Java threads running
132+
* which do not terminate after receiving the Thread.interrupt() event.
133+
* To prevent indefinite blocking, these threads should be cooperatively shut down
134+
* within Java before invoking this call.
135+
* To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' to detect
136+
* the threads that are still running.
137+
* This will print the stack traces of all threads that block tear-down.
130138
*/
131139
typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
132140

@@ -141,6 +149,14 @@ typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread
141149
* Java code at the time when this function is called or at any point in the future
142150
* or this will cause entirely undefined (and likely fatal) behavior.
143151
* Returns 0 on success, or a non-zero value on (non-fatal) failure.
152+
*
153+
* If this call blocks indefinitely, this means there are still Java threads running
154+
* which do not terminate after receiving the Thread.interrupt() event.
155+
* To prevent indefinite blocking, these threads should be cooperatively shut down
156+
* within Java before invoking this call.
157+
* To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' to detect
158+
* the threads that are still running.
159+
* This will print the stack traces of all threads that block tear-down.
144160
*/
145161
typedef int (*graal_detach_all_threads_and_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
146162

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/SubstrateOptions.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package com.oracle.svm.core;
2626

27+
import static com.oracle.svm.core.SubstrateOptions.DeprecatedOptions.TearDownFailureNanos;
2728
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable;
2829
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RegisterForIsolateArgumentParser;
2930
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates;
@@ -72,6 +73,7 @@
7273
import com.oracle.svm.core.option.SubstrateOptionsParser;
7374
import com.oracle.svm.core.pltgot.PLTGOTConfiguration;
7475
import com.oracle.svm.core.thread.VMOperationControl;
76+
import com.oracle.svm.core.util.TimeUtils;
7577
import com.oracle.svm.core.util.UserError;
7678
import com.oracle.svm.core.util.VMError;
7779
import com.oracle.svm.util.LogUtils;
@@ -608,6 +610,18 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
608610
}
609611
}
610612
};
613+
614+
public static final String TEAR_DOWN_WARNING_NANOS_ERROR = "Can't set both TearDownWarningSeconds and TearDownWarningNanos at the same time. Use TearDownWarningSeconds.";
615+
@Option(help = "The number of nanoseconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.", //
616+
deprecated = true, deprecationMessage = "Use -XX:TearDownWarningSeconds=<secs> instead")//
617+
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L,
618+
(key) -> UserError.guarantee(!(key.hasBeenSet() && TearDownWarningSeconds.hasBeenSet()), TEAR_DOWN_WARNING_NANOS_ERROR),
619+
RelevantForCompilationIsolates);
620+
621+
@Option(help = "The number of nanoseconds before tearing down an isolate gives a failure message and returns from a tear-down call. 0 implies no message.", //
622+
deprecated = true, deprecationMessage = "This call leaks resources. Instead, terminate java threads cooperatively, or use System#exit")//
623+
public static final RuntimeOptionKey<Long> TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
624+
611625
}
612626

613627
@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
@@ -862,15 +876,17 @@ private static void validateZapNativeMemory(HostedOptionKey<Boolean> optionKey)
862876
*/
863877

864878
@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
865-
@Option(help = "The number of nanoseconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
866-
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
867-
868-
@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
869-
@Option(help = "The number of nanoseconds before tearing down an isolate gives a failure message. 0 implies no message.")//
870-
public static final RuntimeOptionKey<Long> TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
879+
@Option(help = "The number of seconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
880+
public static final RuntimeOptionKey<Long> TearDownWarningSeconds = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
871881

872882
public static long getTearDownWarningNanos() {
873-
return TearDownWarningNanos.getValue();
883+
if (TearDownWarningSeconds.hasBeenSet() && DeprecatedOptions.TearDownWarningNanos.hasBeenSet()) {
884+
throw new IllegalArgumentException(DeprecatedOptions.TEAR_DOWN_WARNING_NANOS_ERROR);
885+
}
886+
if (DeprecatedOptions.TearDownWarningNanos.hasBeenSet()) {
887+
return DeprecatedOptions.TearDownWarningNanos.getValue();
888+
}
889+
return TearDownWarningSeconds.getValue() * TimeUtils.nanosPerSecond;
874890
}
875891

876892
public static long getTearDownFailureNanos() {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/c/function/CEntryPointNativeFunctions.java

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import java.util.function.Function;
2828

29-
import jdk.graal.compiler.word.Word;
3029
import org.graalvm.nativeimage.CurrentIsolate;
3130
import org.graalvm.nativeimage.Isolate;
3231
import org.graalvm.nativeimage.IsolateThread;
@@ -41,6 +40,8 @@
4140
import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue;
4241
import com.oracle.svm.core.thread.VMThreads;
4342

43+
import jdk.graal.compiler.word.Word;
44+
4445
@CHeader(value = GraalIsolateHeader.class)
4546
public final class CEntryPointNativeFunctions {
4647

@@ -158,12 +159,25 @@ public static int detachThread(IsolateThread thread) {
158159
return CEntryPointActions.leaveDetachThread();
159160
}
160161

162+
static final String THREAD_TERMINATION_NOTE = """
163+
If this call blocks indefinitely, this means there are still Java threads running
164+
which do not terminate after receiving the Thread.interrupt() event.
165+
To prevent indefinite blocking, these threads should be cooperatively shut down
166+
within Java before invoking this call.
167+
To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' to detect
168+
the threads that are still running.
169+
This will print the stack traces of all threads that block tear-down.
170+
""";
171+
161172
@Uninterruptible(reason = UNINTERRUPTIBLE_REASON)
162-
@CEntryPoint(name = "tear_down_isolate", documentation = {
163-
"Tears down the isolate of the passed (and still attached) isolate thread,",
164-
"waiting for any attached threads to detach from it, then discards its objects,",
165-
"threads, and any other state or context that is associated with it.",
166-
"Returns 0 on success, or a non-zero value on failure."})
173+
@CEntryPoint(name = "tear_down_isolate", documentation = {"""
174+
Tears down the isolate of the passed (and still attached) isolate thread,
175+
waiting for any attached threads to detach from it, then discards its objects,
176+
threads, and any other state or context that is associated with it.
177+
Returns 0 on success, or a non-zero value on failure.
178+
""",
179+
THREAD_TERMINATION_NOTE,
180+
})
167181
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
168182
public static int tearDownIsolate(IsolateThread isolateThread) {
169183
int result = CEntryPointActions.enter(isolateThread);
@@ -174,17 +188,20 @@ public static int tearDownIsolate(IsolateThread isolateThread) {
174188
}
175189

176190
@Uninterruptible(reason = UNINTERRUPTIBLE_REASON)
177-
@CEntryPoint(name = "detach_all_threads_and_tear_down_isolate", documentation = {
178-
"In the isolate of the passed isolate thread, detach all those threads that were",
179-
"externally started (not within Java, which includes the \"main thread\") and were",
180-
"attached to the isolate afterwards. Afterwards, all threads that were started",
181-
"within Java undergo a regular shutdown process, followed by the tear-down of the",
182-
"entire isolate, which detaches the current thread and discards the objects,",
183-
"threads, and any other state or context associated with the isolate.",
184-
"None of the manually attached threads targeted by this function may be executing",
185-
"Java code at the time when this function is called or at any point in the future",
186-
"or this will cause entirely undefined (and likely fatal) behavior.",
187-
"Returns 0 on success, or a non-zero value on (non-fatal) failure."})
191+
@CEntryPoint(name = "detach_all_threads_and_tear_down_isolate", documentation = {"""
192+
In the isolate of the passed isolate thread, detach all those threads that were
193+
externally started (not within Java, which includes the "main thread") and were
194+
attached to the isolate afterwards. Afterwards, all threads that were started
195+
within Java undergo a regular shutdown process, followed by the tear-down of the
196+
entire isolate, which detaches the current thread and discards the objects,
197+
threads, and any other state or context associated with the isolate.
198+
None of the manually attached threads targeted by this function may be executing
199+
Java code at the time when this function is called or at any point in the future
200+
or this will cause entirely undefined (and likely fatal) behavior.
201+
Returns 0 on success, or a non-zero value on (non-fatal) failure.
202+
""",
203+
THREAD_TERMINATION_NOTE,
204+
})
188205
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
189206
public static int detachAllThreadsAndTearDownIsolate(IsolateThread isolateThread) {
190207
int result = CEntryPointActions.enter(isolateThread);

0 commit comments

Comments
 (0)