Skip to content

Commit 398cc1c

Browse files
committed
Thread termination note in isolate tear down
1 parent 9d87c20 commit 398cc1c

File tree

3 files changed

+73
-23
lines changed

3 files changed

+73
-23
lines changed

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

+12
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,12 @@ 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-application threads running.
132+
* To prevent indefinite blocking, these threads must be cooperatively shut down within Java before invoking this call.
133+
* To fix this use the option '-R:TearDownWarningSeconds=<secs>' at image build time to detect the threads that are still running.
134+
* This will print the stack traces of all threads that have not been cooperatively terminated.
135+
* Those threads need to be terminated from Java before calling tear_down_isolate.
130136
*/
131137
typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
132138

@@ -141,6 +147,12 @@ typedef int (*graal_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread
141147
* Java code at the time when this function is called or at any point in the future
142148
* or this will cause entirely undefined (and likely fatal) behavior.
143149
* Returns 0 on success, or a non-zero value on (non-fatal) failure.
150+
*
151+
* If this call blocks indefinitely, this means there are still Java-application threads running.
152+
* To prevent indefinite blocking, these threads must be cooperatively shut down within Java before invoking this call.
153+
* To fix this use the option '-R:TearDownWarningSeconds=<secs>' at image build time to detect the threads that are still running.
154+
* This will print the stack traces of all threads that have not been cooperatively terminated.
155+
* Those threads need to be terminated from Java before calling tear_down_isolate.
144156
*/
145157
typedef int (*graal_detach_all_threads_and_tear_down_isolate_fn_t)(graal_isolatethread_t* isolateThread);
146158

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

+28-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,30 @@ 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, 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, RelevantForCompilationIsolates);
826836

827837
public static long getTearDownWarningNanos() {
828-
return TearDownWarningNanos.getValue();
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 (TearDownWarningNanos.hasBeenSet()) {
842+
return TearDownWarningNanos.getValue();
843+
}
844+
return TearDownWarningSeconds.getValue() * TimeUtils.nanosPerSecond;
829845
}
830846

831847
public static long getTearDownFailureNanos() {
832-
return TearDownFailureNanos.getValue();
848+
if (TearDownFailureSeconds.hasBeenSet() && TearDownFailureNanos.hasBeenSet()) {
849+
throw new IllegalArgumentException("Can't set both TearDownFailureSeconds and TearDownFailureNanos at the same time. Use TearDownFailureSeconds.");
850+
}
851+
if (TearDownFailureNanos.hasBeenSet()) {
852+
return TearDownFailureNanos.getValue();
853+
}
854+
return TearDownFailureSeconds.getValue() * TimeUtils.nanosPerSecond;
833855
}
834856

835857
@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

+33-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,24 @@ 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-application threads running.
164+
To prevent indefinite blocking, these threads must be cooperatively shut down within Java before invoking this call.
165+
To fix this use the option '-R:TearDownWarningSeconds=<secs>' at image build time to detect the threads that are still running.
166+
This will print the stack traces of all threads that have not been cooperatively terminated.
167+
Those threads need to be terminated from Java before calling tear_down_isolate.
168+
""";
169+
161170
@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."})
171+
@CEntryPoint(name = "tear_down_isolate", documentation = {"""
172+
Tears down the isolate of the passed (and still attached) isolate thread,
173+
waiting for any attached threads to detach from it, then discards its objects,
174+
threads, and any other state or context that is associated with it.
175+
Returns 0 on success, or a non-zero value on failure.
176+
177+
""",
178+
THREAD_TERMINATION_NOTE,
179+
})
167180
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class, nameTransformation = NameTransformation.class)
168181
public static int tearDownIsolate(IsolateThread isolateThread) {
169182
int result = CEntryPointActions.enter(isolateThread);
@@ -174,17 +187,20 @@ public static int tearDownIsolate(IsolateThread isolateThread) {
174187
}
175188

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

0 commit comments

Comments
 (0)