Skip to content

Commit 330aed9

Browse files
[GR-63494] [GR-63728] Simplify the safepoint code and only enable recurring callback support on demand.
PullRequest: graal/20464
2 parents 0028f47 + 06dc7b5 commit 330aed9

File tree

23 files changed

+611
-488
lines changed

23 files changed

+611
-488
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/Threading.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2017, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -55,13 +55,22 @@ private Threading() {
5555
}
5656

5757
/**
58+
* This method is intended for expert users.
59+
* <p>
5860
* Registers a {@link RecurringCallback callback handler} that is called by the current thread
59-
* approximately at the provided interval. Only one callback can be active per thread. Each
60-
* thread can have its own callback with a different interval (or none at all). No guarantees
61-
* are made about the actual interval. For example, when the thread is waiting for a lock or
62-
* executing native code, no callback can be done. Exceptions that are thrown during the
63-
* execution of the callback are caught and ignored, unless they are thrown via a call to
64-
* {@link RecurringCallbackAccess#throwException(Throwable)}.
61+
* approximately at the provided interval. This functionality is only supported if the native
62+
* binary is built with {@code -H:+SupportRecurringCallback}. Note that only carefully crafted,
63+
* uninterruptible code can execute safely in a recurring callback. Executing any other code
64+
* easily results in deadlocks, crashes, and difficult-to-debug anomalies.
65+
* <p>
66+
* Only one callback can be active per thread. Each thread can have its own callback with a
67+
* different interval (or none at all). No guarantees are made about the actual interval. For
68+
* example, when the thread is waiting for a lock or executing native code, no callback can be
69+
* done.
70+
* <p>
71+
* Exceptions that are thrown during the execution of the callback and that are not caught in
72+
* the callback are ignored. {@link RecurringCallbackAccess#throwException} can be used to
73+
* explicitly throw an exception that is not ignored.
6574
* <p>
6675
* Specifying {@code null} for {@code callback} clears the current thread's callback (in which
6776
* case, the values of {@code interval} and {@code unit} are ignored).

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ This changelog summarizes major changes to GraalVM Native Image.
1414
* (GR-58659) (GR-58660) Support for FFM API ("Panama") has been added for darwin-aarch64 and linux-aarch64.
1515
* (GR-49525) Introduced `--future-defaults=[all|<options>|none]` that enables options that are planned to become defaults in future releases. The enabled options are:
1616
1. `run-time-initialized-jdk` shifts away from build-time initialization of the JDK, instead initializing most of it at run time. This transition is gradual, with individual components of the JDK becoming run-time initialized in each release. This process should complete with JDK 29 when this option should not be needed anymore. Unless you store classes from the JDK in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages.
17+
* (GR-63494) Recurring callback support is no longer enabled by default. If this feature is needed, please specify `-H:+SupportRecurringCallback` at image build-time.
1718

1819
## GraalVM for JDK 24 (Internal Version 24.2.0)
1920
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.

substratevm/src/com.oracle.svm.core.graal.aarch64/src/com/oracle/svm/core/graal/aarch64/AArch64SafepointCheckOp.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@
2626

2727
import com.oracle.svm.core.ReservedRegisters;
2828
import com.oracle.svm.core.nodes.SafepointCheckNode;
29+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
2930
import com.oracle.svm.core.thread.Safepoint;
3031
import com.oracle.svm.core.thread.SafepointCheckCounter;
3132
import com.oracle.svm.core.thread.SafepointSlowpath;
32-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
3333

3434
import jdk.graal.compiler.asm.aarch64.AArch64Address;
3535
import jdk.graal.compiler.asm.aarch64.AArch64Assembler;
@@ -60,7 +60,7 @@ public void emitCode(CompilationResultBuilder crb, AArch64MacroAssembler masm) {
6060
try (ScratchRegister scratchRegister = masm.getScratchRegister()) {
6161
Register scratch = scratchRegister.getRegister();
6262
masm.ldr(safepointSize, scratch, safepointAddress);
63-
if (ThreadingSupportImpl.isRecurringCallbackSupported()) {
63+
if (RecurringCallbackSupport.isEnabled()) {
6464
/* Before subtraction, the counter is compared against 1. */
6565
masm.subs(safepointSize, scratch, scratch, 1);
6666
masm.str(safepointSize, scratch, safepointAddress);

substratevm/src/com.oracle.svm.core.graal.amd64/src/com/oracle/svm/core/graal/amd64/AMD64SafepointCheckOp.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@
2626

2727
import com.oracle.svm.core.ReservedRegisters;
2828
import com.oracle.svm.core.nodes.SafepointCheckNode;
29+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
2930
import com.oracle.svm.core.thread.SafepointCheckCounter;
30-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
3131

3232
import jdk.graal.compiler.asm.amd64.AMD64Address;
3333
import jdk.graal.compiler.asm.amd64.AMD64Assembler;
@@ -53,7 +53,7 @@ public AMD64SafepointCheckOp() {
5353
public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
5454
int counterOffset = SafepointCheckCounter.getThreadLocalOffset();
5555
AMD64Address counter = new AMD64Address(ReservedRegisters.singleton().getThreadRegister(), counterOffset);
56-
if (ThreadingSupportImpl.isRecurringCallbackSupported()) {
56+
if (RecurringCallbackSupport.isEnabled()) {
5757
masm.subl(counter, 1);
5858
} else {
5959
masm.cmpl(counter, 0);

substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/NodeLLVMBuilder.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@
6363
import com.oracle.svm.core.meta.SharedField;
6464
import com.oracle.svm.core.meta.SubstrateObjectConstant;
6565
import com.oracle.svm.core.nodes.SafepointCheckNode;
66+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
6667
import com.oracle.svm.core.thread.SafepointCheckCounter;
67-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
6868
import com.oracle.svm.core.thread.VMThreads;
6969
import com.oracle.svm.core.util.VMError;
7070
import com.oracle.svm.shadowed.org.bytedeco.llvm.LLVM.LLVMBasicBlockRef;
@@ -360,7 +360,7 @@ private LLVMValueRef emitCondition(LogicNode condition) {
360360
threadData = builder.buildIntToPtr(threadData, builder.rawPointerType());
361361
LLVMValueRef safepointCounterAddr = builder.buildGEP(threadData, builder.constantInt(SafepointCheckCounter.getThreadLocalOffset()));
362362
LLVMValueRef safepointCount = builder.buildLoad(safepointCounterAddr, builder.intType());
363-
if (ThreadingSupportImpl.isRecurringCallbackSupported()) {
363+
if (RecurringCallbackSupport.isEnabled()) {
364364
safepointCount = builder.buildSub(safepointCount, builder.constantInt(1));
365365
builder.buildStore(safepointCount, builder.buildBitcast(safepointCounterAddr, builder.pointerType(builder.intType())));
366366
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
public final class BuildPhaseProvider {
3535

3636
private boolean featureRegistrationFinished;
37+
private boolean setupFinished;
3738
private boolean analysisFinished;
3839
private boolean hostedUniverseBuilt;
3940
private boolean readyForCompilation;
@@ -60,6 +61,14 @@ public static boolean isFeatureRegistrationFinished() {
6061
return ImageSingletons.contains(BuildPhaseProvider.class) && singleton().featureRegistrationFinished;
6162
}
6263

64+
public static void markSetupFinished() {
65+
singleton().setupFinished = true;
66+
}
67+
68+
public static boolean isSetupFinished() {
69+
return ImageSingletons.contains(BuildPhaseProvider.class) && singleton().setupFinished;
70+
}
71+
6372
public static void markAnalysisFinished() {
6473
singleton().analysisFinished = true;
6574
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
import com.oracle.svm.core.log.Log;
7777
import com.oracle.svm.core.thread.JavaThreads;
7878
import com.oracle.svm.core.thread.PlatformThreads;
79-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
79+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
8080
import com.oracle.svm.core.thread.VMThreads;
8181
import com.oracle.svm.core.thread.VMThreads.OSThreadHandle;
8282
import com.oracle.svm.core.util.UserError;
@@ -268,7 +268,7 @@ private static int runCore0() {
268268

269269
@Uninterruptible(reason = "The caller initialized the thread state, so the callees do not need to be uninterruptible.", calleeMustBe = false)
270270
private static void runShutdown() {
271-
ThreadingSupportImpl.pauseRecurringCallback("Recurring callbacks can't be executed during shutdown.");
271+
RecurringCallbackSupport.suspendCallbackTimer("Recurring callbacks can't be executed during shutdown.");
272272
runShutdown0();
273273
}
274274

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@
9595
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
9696
import com.oracle.svm.core.stack.StackOverflowCheck;
9797
import com.oracle.svm.core.thread.PlatformThreads;
98+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
9899
import com.oracle.svm.core.thread.ThreadListenerSupport;
99100
import com.oracle.svm.core.thread.ThreadStatusTransition;
100-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
101101
import com.oracle.svm.core.thread.VMOperationControl;
102102
import com.oracle.svm.core.thread.VMThreads;
103103
import com.oracle.svm.core.thread.VMThreads.SafepointBehavior;
@@ -666,7 +666,7 @@ private static int tearDownIsolate() {
666666
}
667667

668668
/* After threadExit(), only uninterruptible code may be executed. */
669-
ThreadingSupportImpl.pauseRecurringCallback("Execution of arbitrary code is prohibited during the last teardown steps.");
669+
RecurringCallbackSupport.suspendCallbackTimer("Execution of arbitrary code is prohibited during the last teardown steps.");
670670

671671
/* Shut down VM thread. */
672672
if (VMOperationControl.useDedicatedVMOperationThread()) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
import com.oracle.svm.core.stack.JavaStackWalker;
5050
import com.oracle.svm.core.stack.StackFrameVisitor;
5151
import com.oracle.svm.core.thread.PlatformThreads;
52-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
52+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
5353
import com.oracle.svm.core.thread.VMOperation;
5454
import com.oracle.svm.core.thread.VMThreads.SafepointBehavior;
5555
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
@@ -118,7 +118,7 @@ public static void deoptTest() {
118118
try {
119119
if (Heap.getHeap().isAllocationDisallowed() ||
120120
!CEntryPointSnippets.isIsolateInitialized() ||
121-
ThreadingSupportImpl.isRecurringCallbackPaused() ||
121+
(RecurringCallbackSupport.isEnabled() && RecurringCallbackSupport.isCallbackTimerSuspended()) ||
122122
VMOperation.isInProgress() ||
123123
SafepointBehavior.ignoresSafepoints() ||
124124
!PlatformThreads.isCurrentAssigned()) {

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Map;
3333
import java.util.function.Predicate;
3434

35-
import jdk.graal.compiler.word.Word;
3635
import org.graalvm.nativeimage.ImageInfo;
3736
import org.graalvm.nativeimage.Platform;
3837
import org.graalvm.nativeimage.Platforms;
@@ -56,7 +55,7 @@
5655
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
5756
import com.oracle.svm.core.stack.StackOverflowCheck;
5857
import com.oracle.svm.core.thread.PlatformThreads;
59-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
58+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
6059
import com.oracle.svm.core.thread.VMThreads;
6160
import com.oracle.svm.core.threadlocal.FastThreadLocal;
6261
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
@@ -91,6 +90,7 @@
9190
import jdk.graal.compiler.replacements.SnippetTemplate.Arguments;
9291
import jdk.graal.compiler.replacements.SnippetTemplate.SnippetInfo;
9392
import jdk.graal.compiler.replacements.Snippets;
93+
import jdk.graal.compiler.word.Word;
9494
import jdk.vm.ci.meta.ResolvedJavaMethod;
9595

9696
public final class StackOverflowCheckImpl implements StackOverflowCheck {
@@ -201,7 +201,7 @@ private static void onYellowZoneMadeAvailable(int oldState, int newState) {
201201
* a recurring callback in the yellow zone is dangerous because a stack overflow in the
202202
* recurring callback would then lead to a fatal error.
203203
*/
204-
ThreadingSupportImpl.pauseRecurringCallback("Recurring callbacks are considered user code and must not run in yellow zone");
204+
RecurringCallbackSupport.suspendCallbackTimer("Recurring callbacks are considered user code and must not run in yellow zone");
205205

206206
stackBoundaryTL.set(stackBoundaryTL.get().subtract(Options.StackYellowZoneSize.getValue()));
207207
}
@@ -241,7 +241,7 @@ private static void onYellowZoneProtected(int oldState, int newState) {
241241
VMError.guarantee(newState < oldState && newState >= STATE_YELLOW_ENABLED, "StackOverflowCheckImpl.onYellowZoneProtected: Illegal state");
242242

243243
if (newState == STATE_YELLOW_ENABLED) {
244-
ThreadingSupportImpl.resumeRecurringCallbackAtNextSafepoint();
244+
RecurringCallbackSupport.resumeCallbackTimerAtNextSafepointCheck();
245245

246246
stackBoundaryTL.set(stackBoundaryTL.get().add(Options.StackYellowZoneSize.getValue()));
247247
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
import com.oracle.svm.core.Uninterruptible;
3737
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
3838
import com.oracle.svm.core.feature.InternalFeature;
39-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
39+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
4040
import com.oracle.svm.core.thread.VMThreads;
4141
import com.oracle.svm.core.util.VMError;
4242

@@ -76,7 +76,7 @@ public static boolean isReferenceHandlerThread(Thread other) {
7676

7777
@Override
7878
public void run() {
79-
ThreadingSupportImpl.pauseRecurringCallback("An exception in a recurring callback must not interrupt pending reference processing because it could result in a memory leak.");
79+
RecurringCallbackSupport.suspendCallbackTimer("An exception in a recurring callback must not interrupt pending reference processing because it could result in a memory leak.");
8080

8181
this.isolateThread = CurrentIsolate.getCurrentThread();
8282
try {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/dump/HeapDumpWriter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@
8383
import com.oracle.svm.core.stack.JavaStackWalker;
8484
import com.oracle.svm.core.stack.StackFrameVisitor;
8585
import com.oracle.svm.core.thread.PlatformThreads;
86-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
86+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
8787
import com.oracle.svm.core.thread.VMOperation;
8888
import com.oracle.svm.core.thread.VMThreads;
8989
import com.oracle.svm.core.threadlocal.VMThreadLocalSupport;
@@ -421,7 +421,7 @@ public HeapDumpWriter(HeapDumpMetadata metadata) {
421421

422422
public boolean dumpHeap(RawFileDescriptor fd) {
423423
assert VMOperation.isInProgressAtSafepoint();
424-
assert ThreadingSupportImpl.isRecurringCallbackPaused();
424+
assert RecurringCallbackSupport.isCallbackUnsupportedOrTimerSuspended();
425425

426426
noAllocationVerifier.open();
427427
try {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrChunkFileWriter.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,14 @@
2929

3030
import java.nio.charset.StandardCharsets;
3131

32-
import com.oracle.svm.core.jfr.oldobject.JfrOldObjectRepository;
33-
import jdk.graal.compiler.word.Word;
3432
import org.graalvm.nativeimage.IsolateThread;
3533
import org.graalvm.nativeimage.Platform;
3634
import org.graalvm.nativeimage.Platforms;
3735
import org.graalvm.word.UnsignedWord;
3836

3937
import com.oracle.svm.core.Uninterruptible;
4038
import com.oracle.svm.core.heap.VMOperationInfos;
39+
import com.oracle.svm.core.jfr.oldobject.JfrOldObjectRepository;
4140
import com.oracle.svm.core.jfr.sampler.JfrExecutionSampler;
4241
import com.oracle.svm.core.jfr.sampler.JfrRecurringCallbackExecutionSampler;
4342
import com.oracle.svm.core.jfr.traceid.JfrTraceIdEpoch;
@@ -48,13 +47,14 @@
4847
import com.oracle.svm.core.os.RawFileOperationSupport.RawFileDescriptor;
4948
import com.oracle.svm.core.sampler.SamplerBuffersAccess;
5049
import com.oracle.svm.core.thread.JavaVMOperation;
51-
import com.oracle.svm.core.thread.ThreadingSupportImpl;
50+
import com.oracle.svm.core.thread.RecurringCallbackSupport;
5251
import com.oracle.svm.core.thread.VMOperation;
5352
import com.oracle.svm.core.thread.VMOperationControl;
5453
import com.oracle.svm.core.thread.VMThreads;
5554

5655
import jdk.graal.compiler.api.replacements.Fold;
5756
import jdk.graal.compiler.core.common.NumUtil;
57+
import jdk.graal.compiler.word.Word;
5858

5959
/**
6060
* This class is used when writing the in-memory JFR data to a file. For all operations, except
@@ -673,7 +673,7 @@ private void changeEpoch() {
673673
@Uninterruptible(reason = "Prevent JFR recording.")
674674
private static void processSamplerBuffers() {
675675
assert VMOperation.isInProgressAtSafepoint();
676-
assert ThreadingSupportImpl.isRecurringCallbackPaused();
676+
assert RecurringCallbackSupport.isCallbackUnsupportedOrTimerSuspended();
677677

678678
JfrExecutionSampler.singleton().disallowThreadsInSamplerCode();
679679
try {

0 commit comments

Comments
 (0)