Skip to content

Commit f8d282b

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

File tree

3 files changed

+64
-20
lines changed

3 files changed

+64
-20
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ 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 which do not terminate after receiving the Thread.interrupt() event.
132+
* To prevent indefinite blocking, these threads should be cooperatively shut down within Java before invoking this call.
133+
* To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' to detect the threads that are still running.
134+
* This will print the stack traces of all threads that block tear-down.
130135
*/
131136
typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
132137

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

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

Lines changed: 23 additions & 3 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)//
@@ -866,11 +880,17 @@ private static void validateZapNativeMemory(HostedOptionKey<Boolean> optionKey)
866880
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
867881

868882
@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);
883+
@Option(help = "The number of seconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
884+
public static final RuntimeOptionKey<Long> TearDownWarningSeconds = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
871885

872886
public static long getTearDownWarningNanos() {
873-
return TearDownWarningNanos.getValue();
887+
if (TearDownWarningSeconds.hasBeenSet() && TearDownWarningNanos.hasBeenSet()) {
888+
throw new IllegalArgumentException(DeprecatedOptions.TEAR_DOWN_WARNING_NANOS_ERROR);
889+
}
890+
if (TearDownWarningNanos.hasBeenSet()) {
891+
return TearDownWarningNanos.getValue();
892+
}
893+
return TearDownWarningSeconds.getValue() * TimeUtils.nanosPerSecond;
874894
}
875895

876896
public static long getTearDownFailureNanos() {

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

Lines changed: 31 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,22 @@ 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 which do not terminate after receiving the Thread.interrupt() event.
164+
To prevent indefinite blocking, these threads should be cooperatively shut down within Java before invoking this call.
165+
To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' to detect the threads that are still running.
166+
This will print the stack traces of all threads that block tear-down.
167+
""";
168+
161169
@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."})
170+
@CEntryPoint(name = "tear_down_isolate", documentation = {"""
171+
Tears down the isolate of the passed (and still attached) isolate thread,
172+
waiting for any attached threads to detach from it, then discards its objects,
173+
threads, and any other state or context that is associated with it.
174+
Returns 0 on success, or a non-zero value on failure.
175+
""",
176+
THREAD_TERMINATION_NOTE,
177+
})
167178
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
168179
public static int tearDownIsolate(IsolateThread isolateThread) {
169180
int result = CEntryPointActions.enter(isolateThread);
@@ -174,17 +185,20 @@ public static int tearDownIsolate(IsolateThread isolateThread) {
174185
}
175186

176187
@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."})
188+
@CEntryPoint(name = "detach_all_threads_and_tear_down_isolate", documentation = {"""
189+
In the isolate of the passed isolate thread, detach all those threads that were
190+
externally started (not within Java, which includes the "main thread") and were
191+
attached to the isolate afterwards. Afterwards, all threads that were started
192+
within Java undergo a regular shutdown process, followed by the tear-down of the
193+
entire isolate, which detaches the current thread and discards the objects,
194+
threads, and any other state or context associated with the isolate.
195+
None of the manually attached threads targeted by this function may be executing
196+
Java code at the time when this function is called or at any point in the future
197+
or this will cause entirely undefined (and likely fatal) behavior.
198+
Returns 0 on success, or a non-zero value on (non-fatal) failure.
199+
""",
200+
THREAD_TERMINATION_NOTE,
201+
})
188202
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
189203
public static int detachAllThreadsAndTearDownIsolate(IsolateThread isolateThread) {
190204
int result = CEntryPointActions.enter(isolateThread);

0 commit comments

Comments
 (0)