Skip to content

Commit 9827c2f

Browse files
committed
Thread termination note in isolate tear down
1 parent eed8931 commit 9827c2f

File tree

3 files changed

+86
-23
lines changed

3 files changed

+86
-23
lines changed

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

+18
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,15 @@ 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+
*
132+
* If this call blocks indefinitely, this means there are still Java threads running
133+
* that do not terminate after receiving a Thread.interrupt() event. To prevent indefinite blocking,
134+
* these threads should be cooperatively terminated within Java before invoking this call.
135+
* To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' at image build time
136+
* to detect the threads that are still running. This will print the stack traces of all threads that block tear-down.
137+
* To resolve blocking issues, utilize any available method to terminate the offending threads. This may include
138+
* calling shutdown API functions, adjusting the application configuration, or leveraging reflection.
130139
*/
131140
typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
132141

@@ -141,6 +150,15 @@ typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread
141150
* Java code at the time when this function is called or at any point in the future
142151
* or this will cause entirely undefined (and likely fatal) behavior.
143152
* Returns 0 on success, or a non-zero value on (non-fatal) failure.
153+
*
154+
*
155+
* If this call blocks indefinitely, this means there are still Java threads running
156+
* that do not terminate after receiving a Thread.interrupt() event. To prevent indefinite blocking,
157+
* these threads should be cooperatively terminated within Java before invoking this call.
158+
* To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' at image build time
159+
* to detect the threads that are still running. This will print the stack traces of all threads that block tear-down.
160+
* To resolve blocking issues, utilize any available method to terminate the offending threads. This may include
161+
* calling shutdown API functions, adjusting the application configuration, or leveraging reflection.
144162
*/
145163
typedef int (*graal_detach_all_threads_and_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
146164

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

+33-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
*/
2525
package com.oracle.svm.core;
2626

27+
import static com.oracle.svm.core.SubstrateOptions.DeprecatedOptions.TearDownFailureNanos;
28+
import static com.oracle.svm.core.SubstrateOptions.DeprecatedOptions.TearDownWarningNanos;
2729
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable;
2830
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.IsolateCreationOnly;
2931
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates;
@@ -63,6 +65,7 @@
6365
import com.oracle.svm.core.option.RuntimeOptionKey;
6466
import com.oracle.svm.core.option.SubstrateOptionsParser;
6567
import com.oracle.svm.core.thread.VMOperationControl;
68+
import com.oracle.svm.core.util.TimeUtils;
6669
import com.oracle.svm.core.util.UserError;
6770
import com.oracle.svm.util.LogUtils;
6871
import com.oracle.svm.util.ModuleSupport;
@@ -576,6 +579,13 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
576579
}
577580
}
578581
};
582+
583+
@Option(help = "The number of nanoseconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.", deprecated = true, deprecationMessage = "Use TearDownWarningSeconds instead")//
584+
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
585+
586+
@Option(help = "The number of nanoseconds before tearing down an isolate gives a failure message. 0 implies no message.", deprecated = true, deprecationMessage = "Use TearDownFailureSeconds instead")//
587+
public static final RuntimeOptionKey<Long> TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
588+
579589
}
580590

581591
@Option(help = "Enable detection and runtime container configuration support.")//
@@ -818,18 +828,35 @@ private static void validateZapNativeMemory(HostedOptionKey<Boolean> optionKey)
818828
* Isolate tear down options.
819829
*/
820830

821-
@Option(help = "The number of nanoseconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
822-
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
831+
@Option(help = "The number of seconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
832+
public static final RuntimeOptionKey<Long> TearDownWarningSeconds = new RuntimeOptionKey<>(0L, key -> validateTearDownTimeoutOptions(), RelevantForCompilationIsolates);
823833

824-
@Option(help = "The number of nanoseconds before tearing down an isolate gives a failure message. 0 implies no message.")//
825-
public static final RuntimeOptionKey<Long> TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
834+
@Option(help = "The number of seconds before tearing down an isolate gives a failure message. 0 implies no message.")//
835+
public static final RuntimeOptionKey<Long> TearDownFailureSeconds = new RuntimeOptionKey<>(0L, key -> validateTearDownTimeoutOptions(), RelevantForCompilationIsolates);
836+
837+
private static void validateTearDownTimeoutOptions() {
838+
if (TearDownWarningSeconds.hasBeenSet() && TearDownWarningNanos.hasBeenSet()) {
839+
throw new IllegalArgumentException("Can't set both TearDownWarningSeconds and TearDownWarningNanos at the same time. Use TearDownWarningSeconds.");
840+
}
841+
if (TearDownFailureSeconds.hasBeenSet() && TearDownFailureNanos.hasBeenSet()) {
842+
throw new IllegalArgumentException("Can't set both TearDownFailureSeconds and TearDownFailureNanos at the same time. Use TearDownFailureSeconds.");
843+
}
844+
}
826845

827846
public static long getTearDownWarningNanos() {
828-
return TearDownWarningNanos.getValue();
847+
validateTearDownTimeoutOptions();
848+
if (TearDownWarningNanos.hasBeenSet()) {
849+
return TearDownWarningNanos.getValue();
850+
}
851+
return TearDownWarningSeconds.getValue() * TimeUtils.nanosPerSecond;
829852
}
830853

831854
public static long getTearDownFailureNanos() {
832-
return TearDownFailureNanos.getValue();
855+
validateTearDownTimeoutOptions();
856+
if (TearDownFailureNanos.hasBeenSet()) {
857+
return TearDownFailureNanos.getValue();
858+
}
859+
return TearDownFailureSeconds.getValue() * TimeUtils.nanosPerSecond;
833860
}
834861

835862
@Option(help = "Define the maximum number of stores for which the loop that zeroes out objects is unrolled.")//

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

+35-17
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,26 @@ public static int detachThread(IsolateThread thread) {
158159
return CEntryPointActions.leaveDetachThread();
159160
}
160161

162+
static final String THREAD_TERMINATION_NOTE = """
163+
164+
If this call blocks indefinitely, this means there are still Java threads running
165+
that do not terminate after receiving a Thread.interrupt() event. To prevent indefinite blocking,
166+
these threads should be cooperatively terminated within Java before invoking this call.
167+
To diagnose such issues, use the option '-R:TearDownWarningSeconds=<secs>' at image build time
168+
to detect the threads that are still running. This will print the stack traces of all threads that block tear-down.
169+
To resolve blocking issues, utilize any available method to terminate the offending threads. This may include
170+
calling shutdown API functions, adjusting the application configuration, or leveraging reflection.
171+
""";
172+
161173
@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."})
174+
@CEntryPoint(name = "tear_down_isolate", documentation = {"""
175+
Tears down the isolate of the passed (and still attached) isolate thread,
176+
waiting for any attached threads to detach from it, then discards its objects,
177+
threads, and any other state or context that is associated with it.
178+
Returns 0 on success, or a non-zero value on failure.
179+
""",
180+
THREAD_TERMINATION_NOTE,
181+
})
167182
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
168183
public static int tearDownIsolate(IsolateThread isolateThread) {
169184
int result = CEntryPointActions.enter(isolateThread);
@@ -174,17 +189,20 @@ public static int tearDownIsolate(IsolateThread isolateThread) {
174189
}
175190

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

0 commit comments

Comments
 (0)