Skip to content

Commit 41825fe

Browse files
[GR-67578] Reference handler and VM operation thread fixes.
PullRequest: graal/21556
2 parents b1497a9 + 17b49aa commit 41825fe

File tree

8 files changed

+279
-209
lines changed

8 files changed

+279
-209
lines changed

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

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

27-
import static com.oracle.svm.core.SubstrateOptions.DeprecatedOptions.TearDownFailureNanos;
2827
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.Immutable;
2928
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RegisterForIsolateArgumentParser;
3029
import static com.oracle.svm.core.option.RuntimeOptionKey.RuntimeOptionKeyFlag.RelevantForCompilationIsolates;
@@ -618,17 +617,13 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Boolean o
618617
}
619618
};
620619

621-
public static final String TEAR_DOWN_WARNING_NANOS_ERROR = "Can't set both TearDownWarningSeconds and TearDownWarningNanos at the same time. Use TearDownWarningSeconds.";
622-
@Option(help = "The number of nanoseconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.", //
620+
@Option(help = "The number of nanoseconds that the isolate teardown can take before warnings are printed. Disabled if less or equal to 0.", //
623621
deprecated = true, deprecationMessage = "Use -XX:TearDownWarningSeconds=<secs> instead")//
624-
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L,
625-
(key) -> UserError.guarantee(!(key.hasBeenSet() && TearDownWarningSeconds.hasBeenSet()), TEAR_DOWN_WARNING_NANOS_ERROR),
626-
RelevantForCompilationIsolates);
622+
public static final RuntimeOptionKey<Long> TearDownWarningNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
627623

628-
@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.", //
629-
deprecated = true, deprecationMessage = "This call leaks resources. Instead, terminate java threads cooperatively, or use System#exit")//
624+
@Option(help = "The number of nanoseconds that the isolate teardown can take before a fatal error is thrown. Disabled if less or equal to 0.", //
625+
deprecated = true, deprecationMessage = "Use -XX:TearDownFailureSeconds=<secs> instead")//
630626
public static final RuntimeOptionKey<Long> TearDownFailureNanos = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
631-
632627
}
633628

634629
@LayerVerifiedOption(kind = Kind.Changed, severity = Severity.Error)//
@@ -870,24 +865,18 @@ private static void validateZapNativeMemory(HostedOptionKey<Boolean> optionKey)
870865
}
871866
}
872867

873-
/*
874-
* Isolate tear down options.
875-
*/
876-
@Option(help = "The number of seconds before and between which tearing down an isolate gives a warning message. 0 implies no warning.")//
877-
public static final RuntimeOptionKey<Long> TearDownWarningSeconds = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
878-
879868
public static long getTearDownWarningNanos() {
880-
if (TearDownWarningSeconds.hasBeenSet() && DeprecatedOptions.TearDownWarningNanos.hasBeenSet()) {
881-
throw new IllegalArgumentException(DeprecatedOptions.TEAR_DOWN_WARNING_NANOS_ERROR);
882-
}
883-
if (DeprecatedOptions.TearDownWarningNanos.hasBeenSet()) {
884-
return DeprecatedOptions.TearDownWarningNanos.getValue();
869+
if (ConcealedOptions.TearDownWarningSeconds.getValue() != 0) {
870+
return TimeUtils.secondsToNanos(ConcealedOptions.TearDownWarningSeconds.getValue());
885871
}
886-
return TearDownWarningSeconds.getValue() * TimeUtils.nanosPerSecond;
872+
return DeprecatedOptions.TearDownWarningNanos.getValue();
887873
}
888874

889875
public static long getTearDownFailureNanos() {
890-
return TearDownFailureNanos.getValue();
876+
if (ConcealedOptions.TearDownFailureSeconds.getValue() != 0) {
877+
return TimeUtils.secondsToNanos(ConcealedOptions.TearDownFailureSeconds.getValue());
878+
}
879+
return DeprecatedOptions.TearDownFailureNanos.getValue();
891880
}
892881

893882
@Option(help = "Define the maximum number of stores for which the loop that zeroes out objects is unrolled.")//
@@ -1264,6 +1253,15 @@ protected void onValueUpdate(EconomicMap<OptionKey<?>, Object> values, Integer o
12641253
@Option(help = "Avoid linker relocations for code and instead emit address computations.", type = OptionType.Expert) //
12651254
@LayerVerifiedOption(severity = Severity.Error, kind = Kind.Changed, positional = false) //
12661255
public static final HostedOptionKey<Boolean> RelativeCodePointers = new HostedOptionKey<>(false, SubstrateOptions::validateRelativeCodePointers);
1256+
1257+
/** Use {@link SubstrateOptions#getTearDownWarningNanos()} instead. */
1258+
@Option(help = "The number of seconds that the isolate teardown can take before warnings are printed. Disabled if less or equal to 0.")//
1259+
public static final RuntimeOptionKey<Long> TearDownWarningSeconds = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
1260+
1261+
/** Use {@link SubstrateOptions#getTearDownFailureNanos()} instead. */
1262+
@Option(help = "The number of seconds that the isolate teardown can take before a fatal error is thrown. Disabled if less or equal to 0.")//
1263+
public static final RuntimeOptionKey<Long> TearDownFailureSeconds = new RuntimeOptionKey<>(0L, RelevantForCompilationIsolates);
1264+
12671265
}
12681266

12691267
@Option(help = "Overwrites the available number of processors provided by the OS. Any value <= 0 means using the processor count from the OS.")//

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/graal/snippets/CEntryPointSnippets.java

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -671,14 +671,17 @@ public static int tearDownIsolateSnippet() {
671671
private static int tearDownIsolate() {
672672
try {
673673
/* Execute interruptible code. */
674-
if (!initiateTearDownIsolateInterruptibly()) {
675-
return CEntryPointErrors.UNSPECIFIED;
676-
}
674+
initiateTearDownIsolateInterruptibly();
677675

678676
/* After threadExit(), only uninterruptible code may be executed. */
679677
RecurringCallbackSupport.suspendCallbackTimer("Execution of arbitrary code is prohibited during the last teardown steps.");
680678

681-
/* Shut down VM thread. */
679+
/* Wait until the reference handler thread detaches (it was already stopped earlier). */
680+
if (ReferenceHandler.useDedicatedThread()) {
681+
ReferenceHandlerThread.waitUntilDetached();
682+
}
683+
684+
/* Shut down VM operation thread. */
682685
if (VMOperationControl.useDedicatedVMOperationThread()) {
683686
VMOperationControl.shutdownAndDetachVMOperationThread();
684687
}
@@ -717,15 +720,20 @@ private static int tearDownIsolate() {
717720
}
718721
}
719722

720-
@Uninterruptible(reason = "Tear-down in progress - still safe to execute interruptible Java code.", calleeMustBe = false)
721-
private static boolean initiateTearDownIsolateInterruptibly() {
723+
@Uninterruptible(reason = "Tear-down in progress - still safe to execute interruptible Java code.", callerMustBe = true, calleeMustBe = false)
724+
private static void initiateTearDownIsolateInterruptibly() {
722725
RuntimeSupport.executeTearDownHooks();
723-
if (!PlatformThreads.tearDownOtherThreads()) {
724-
return false;
726+
PlatformThreads.tearDownOtherThreads();
727+
/*
728+
* At this point, only the current thread, the VM operation thread, and the reference
729+
* handler thread are still running.
730+
*/
731+
if (ReferenceHandler.useDedicatedThread()) {
732+
ReferenceHandlerThread.initiateShutdown();
725733
}
726734

727735
VMThreads.singleton().threadExit();
728-
return true;
736+
/* After threadExit(), only uninterruptible code may be executed. */
729737
}
730738

731739
@Snippet(allowMissingProbabilities = true)
@@ -802,7 +810,7 @@ static boolean runtimeAssertionsEnabled() {
802810
@SubstrateForeignCallTarget(stubCallingConvention = false)
803811
private static int verifyIsolateThread(IsolateThread thread) {
804812
VMError.guarantee(CurrentIsolate.getCurrentThread() == thread, "Threads must match for the call below");
805-
if (!VMThreads.singleton().verifyIsCurrentThread(thread) || !VMThreads.singleton().verifyThreadIsAttached(thread)) {
813+
if (!VMThreads.singleton().verifyIsCurrentThread(thread) || !VMThreads.isAttached(thread)) {
806814
throw VMError.shouldNotReachHere("A call from native code to Java code provided the wrong JNI environment or the wrong IsolateThread. " +
807815
"The JNI environment / IsolateThread is a thread-local data structure and must not be shared between threads.");
808816

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandler.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,18 +24,22 @@
2424
*/
2525
package com.oracle.svm.core.heap;
2626

27+
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
28+
2729
import java.lang.ref.Reference;
2830

2931
import com.oracle.svm.core.IsolateArgumentParser;
3032
import com.oracle.svm.core.NeverInline;
3133
import com.oracle.svm.core.SubstrateOptions;
3234
import com.oracle.svm.core.SubstrateUtil;
35+
import com.oracle.svm.core.Uninterruptible;
3336
import com.oracle.svm.core.stack.StackOverflowCheck;
3437
import com.oracle.svm.core.util.VMError;
3538

3639
import jdk.internal.ref.CleanerFactory;
3740

3841
public final class ReferenceHandler {
42+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
3943
public static boolean useDedicatedThread() {
4044
int automaticReferenceHandling = IsolateArgumentParser.getOptionIndex(SubstrateOptions.ConcealedOptions.AutomaticReferenceHandling);
4145
return ReferenceHandlerThread.isSupported() && IsolateArgumentParser.singleton().getBooleanOptionValue(automaticReferenceHandling);

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/ReferenceHandlerThread.java

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@
4141
import com.oracle.svm.core.util.VMError;
4242

4343
import jdk.graal.compiler.api.replacements.Fold;
44+
import jdk.graal.compiler.word.Word;
4445

4546
public final class ReferenceHandlerThread implements Runnable {
4647
private final Thread thread;
4748
private volatile IsolateThread isolateThread;
49+
private volatile boolean stopped;
4850

4951
@Platforms(Platform.HOSTED_ONLY.class)
5052
ReferenceHandlerThread() {
@@ -54,24 +56,48 @@ public final class ReferenceHandlerThread implements Runnable {
5456
}
5557

5658
public static void start() {
57-
if (isSupported()) {
58-
singleton().thread.start();
59+
if (!isSupported()) {
60+
return;
5961
}
62+
63+
singleton().thread.start();
64+
/* Wait until the isolateThread field is initialized. */
65+
while (singleton().isolateThread.isNull()) {
66+
Thread.yield();
67+
}
68+
}
69+
70+
public static void initiateShutdown() {
71+
if (!isSupported()) {
72+
return;
73+
}
74+
75+
singleton().stopped = true;
76+
Heap.getHeap().wakeUpReferencePendingListWaiters();
77+
}
78+
79+
@Uninterruptible(reason = "Executed during teardown after VMThreads#threadExit")
80+
public static void waitUntilDetached() {
81+
if (!isSupported()) {
82+
return;
83+
}
84+
85+
VMThreads.waitInNativeUntilDetached(singleton().isolateThread);
86+
singleton().isolateThread = Word.nullPointer();
6087
}
6188

6289
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
6390
public static boolean isReferenceHandlerThread() {
64-
if (isSupported()) {
65-
return CurrentIsolate.getCurrentThread() == singleton().isolateThread;
66-
}
67-
return false;
91+
return isReferenceHandlerThread(CurrentIsolate.getCurrentThread());
92+
}
93+
94+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
95+
public static boolean isReferenceHandlerThread(IsolateThread other) {
96+
return isSupported() && other == singleton().isolateThread;
6897
}
6998

7099
public static boolean isReferenceHandlerThread(Thread other) {
71-
if (isSupported()) {
72-
return other == singleton().thread;
73-
}
74-
return false;
100+
return isSupported() && other == singleton().thread;
75101
}
76102

77103
@Override
@@ -80,19 +106,13 @@ public void run() {
80106

81107
this.isolateThread = CurrentIsolate.getCurrentThread();
82108
try {
83-
while (true) {
109+
while (!stopped) {
84110
ReferenceInternals.waitForPendingReferences();
85111
ReferenceInternals.processPendingReferences();
86112
ReferenceHandler.processCleaners();
87113
}
88-
} catch (InterruptedException e) {
89-
VMError.guarantee(VMThreads.isTearingDown(), "Reference Handler should only be interrupted during tear-down");
90114
} catch (Throwable t) {
91-
if (t instanceof OutOfMemoryError && VMThreads.isTearingDown()) {
92-
// Likely failed to allocate the InterruptedException, ignore either way.
93-
} else {
94-
throw VMError.shouldNotReachHere("Reference processing and cleaners must handle all potential exceptions", t);
95-
}
115+
throw VMError.shouldNotReachHere("Reference processing and cleaners must handle all potential exceptions", t);
96116
}
97117
}
98118

0 commit comments

Comments
 (0)