Skip to content

Commit 490e1f1

Browse files
[GR-63037] Cleanups and refactorings for STATUS_IN_VM.
PullRequest: graal/20362
2 parents c3aa234 + 1103438 commit 490e1f1

File tree

5 files changed

+76
-71
lines changed

5 files changed

+76
-71
lines changed

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/HeapImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public boolean isAllocationDisallowed() {
262262
/** A guard to place before an allocation, giving the call site and the allocation type. */
263263
static void exitIfAllocationDisallowed(String callSite, String typeName) {
264264
if (HeapImpl.getHeapImpl().isAllocationDisallowed()) {
265-
NoAllocationVerifier.exit(callSite, typeName);
265+
throw NoAllocationVerifier.exit(callSite, typeName);
266266
}
267267
}
268268

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

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,29 +31,40 @@
3131

3232
import org.graalvm.nativeimage.c.function.CFunction;
3333

34+
import com.oracle.svm.core.Uninterruptible;
35+
import com.oracle.svm.core.thread.VMThreads.StatusSupport;
36+
3437
/**
35-
* This annotation is used to override or extend the behavior of {@link CFunction}. Must only be
36-
* used on methods that are annotated with {@link CFunction}.
38+
* This annotation is used to override or extend the behavior of {@link CFunction}. May only be used
39+
* on methods that are annotated with {@link CFunction}.
3740
*/
3841
@Retention(RetentionPolicy.RUNTIME)
3942
@Target(ElementType.METHOD)
4043
public @interface CFunctionOptions {
41-
/**
42-
* Describes the thread state transition performed when the C function is invoked.
43-
*/
4444
enum Transition {
4545
/**
46-
* The thread state is transitioned from Java to VM, and the Java parts of the stack are
47-
* made walkable. If the C code blocks or calls back to Java, it must do an explicit thread
48-
* state transition to native to prevent that safepoints (and therefore garbage collections)
49-
* of other threads are delayed.
46+
* Does a transition to {@link StatusSupport#STATUS_IN_VM}. This prevents safepoints
47+
* (similar to {@code NO_TRANSITION}) but also pushes a frame anchor (similar to {@code
48+
* TO_NATIVE}) to make the Java part of the stack walkable.
49+
*
50+
* The executed C code can safely assume that there are no safepoints happening in the VM.
51+
* If it is necessary to block in the native code, the C code can do an explicit thread
52+
* state transition to {@link StatusSupport#STATUS_IN_NATIVE} to allow safepoints in a
53+
* controlled manner.
54+
*
55+
* Note that this transition does not do a safepoint check when the C code returns back to
56+
* Java. This allows C functions to return raw pointers to Java objects, if the Java caller
57+
* is an {@link Uninterruptible} method.
58+
*
59+
* This transition may only be used by trusted native code as it can result in deadlocks
60+
* easily. Therefore, it is not part of the Native Image API.
5061
*/
5162
TO_VM
5263
}
5364

5465
/**
55-
* The Java-to-C thread transition code used when calling the C function. Overrides the
56-
* transition that is set via the {@link CFunction} annotation.
66+
* The thread state transition performed when calling the C function. Overrides the transition
67+
* that is set via the {@link CFunction} annotation.
5768
*/
5869
Transition transition();
5970
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public class NoAllocationVerifier implements AutoCloseable {
4343
public static final String ERROR_MSG = "Attempt to allocate while allocation was explicitly disabled using a NoAllocationVerifier";
4444

4545
/** A guard to place before an allocation, giving the call site and the allocation type. */
46-
public static void exit(final String callSite, final String typeName) {
46+
public static RuntimeException exit(final String callSite, final String typeName) {
4747
Log.log().string("[NoAllocationVerifier detected disallowed allocation: ").string(callSite).string(": ").string(typeName).newline();
4848
if (openVerifiers.get() != null) {
4949
Log.log().string("[NoAllocationVerifier stack: ");

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/Safepoint.java

Lines changed: 24 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929

3030
import java.util.concurrent.atomic.AtomicInteger;
3131

32-
import jdk.graal.compiler.word.Word;
3332
import org.graalvm.nativeimage.CurrentIsolate;
3433
import org.graalvm.nativeimage.ImageSingletons;
3534
import org.graalvm.nativeimage.IsolateThread;
@@ -53,7 +52,6 @@
5352
import com.oracle.svm.core.log.Log;
5453
import com.oracle.svm.core.nodes.CFunctionEpilogueNode;
5554
import com.oracle.svm.core.nodes.CFunctionPrologueNode;
56-
import com.oracle.svm.core.nodes.CodeSynchronizationNode;
5755
import com.oracle.svm.core.nodes.SafepointCheckNode;
5856
import com.oracle.svm.core.option.HostedOptionKey;
5957
import com.oracle.svm.core.option.RuntimeOptionKey;
@@ -81,6 +79,7 @@
8179
import jdk.graal.compiler.nodes.extended.ForeignCallNode;
8280
import jdk.graal.compiler.nodes.extended.MembarNode;
8381
import jdk.graal.compiler.options.Option;
82+
import jdk.graal.compiler.word.Word;
8483

8584
/**
8685
* Support for initiating safepoints, which are a global state in which all threads are paused so
@@ -151,14 +150,13 @@ public final class Safepoint {
151150
public static final SubstrateForeignCallDescriptor ENTER_SLOW_PATH_SAFEPOINT_CHECK = SnippetRuntime.findForeignCall(Safepoint.class, "enterSlowPathSafepointCheck", NO_SIDE_EFFECT);
152151
public static final SubstrateForeignCallDescriptor ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS = SnippetRuntime.findForeignCall(Safepoint.class,
153152
"enterSlowPathTransitionFromNativeToNewStatus", NO_SIDE_EFFECT);
154-
private static final SubstrateForeignCallDescriptor ENTER_SLOW_PATH_TRANSITION_FROM_VM_TO_JAVA = SnippetRuntime.findForeignCall(Safepoint.class, "enterSlowPathTransitionFromVMToJava",
155-
NO_SIDE_EFFECT);
153+
private static final SubstrateForeignCallDescriptor ENTER_SLOW_PATH_RUN_PENDING_ACTIONS = SnippetRuntime.findForeignCall(Safepoint.class, "enterSlowPathRunPendingActions", NO_SIDE_EFFECT);
156154

157155
/** All foreign calls defined in this class. */
158156
public static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = new SubstrateForeignCallDescriptor[]{
159157
ENTER_SLOW_PATH_SAFEPOINT_CHECK,
160158
ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS,
161-
ENTER_SLOW_PATH_TRANSITION_FROM_VM_TO_JAVA,
159+
ENTER_SLOW_PATH_RUN_PENDING_ACTIONS,
162160
};
163161

164162
/** Private constructor: No instances: only statics. */
@@ -250,32 +248,12 @@ private static void slowPathSafepointCheck0(int newStatus, boolean callerHasJava
250248
}
251249

252250
if (newStatus == StatusSupport.STATUS_IN_JAVA) {
253-
// Resetting the safepoint counter or executing the recurring callback must only be done
254-
// if the thread is in Java state.
255-
slowPathRunJavaStateActions();
251+
ActionOnTransitionToJavaSupport.runPendingActions();
252+
/* Do this last so that a thrown exception cannot skip any of the above. */
253+
ThreadingSupportImpl.onSafepointCheckSlowpath();
256254
}
257255
}
258256

259-
/**
260-
* Slow path code run after a safepoint check or after transitioning from VM to Java state. It
261-
* resets the safepoint counter, runs recurring callbacks if necessary, and executes pending
262-
* {@link ActionOnTransitionToJavaSupport transition actions}.
263-
*/
264-
@Uninterruptible(reason = "Must not contain safepoint checks.")
265-
private static void slowPathRunJavaStateActions() {
266-
if (ActionOnTransitionToJavaSupport.isActionPending()) {
267-
if (ActionOnTransitionToJavaSupport.isSynchronizeCode()) {
268-
CodeSynchronizationNode.synchronizeCode();
269-
} else {
270-
assert false : "Unexpected action pending.";
271-
}
272-
ActionOnTransitionToJavaSupport.clearActions();
273-
}
274-
275-
// Do this last so an exception cannot skip the above
276-
ThreadingSupportImpl.onSafepointCheckSlowpath();
277-
}
278-
279257
@NeverInline("Must not be inlined in a caller that has an exception handler: We only support InvokeNode and not InvokeWithExceptionNode between a CFunctionPrologueNode and CFunctionEpilogueNode")
280258
@Uninterruptible(reason = "Must not contain safepoint checks.")
281259
private static void freezeAtSafepoint(int newStatus, boolean callerHasJavaFrameAnchor) {
@@ -457,7 +435,7 @@ public static void transitionNativeToJava(boolean popFrameAnchor) {
457435
JavaFrameAnchors.popFrameAnchor();
458436
}
459437
} else {
460-
callSlowPathNativeToNewStatus(Safepoint.ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS, newStatus, popFrameAnchor);
438+
callSlowPathNativeToNewStatus(ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS, newStatus, popFrameAnchor);
461439
}
462440

463441
/*
@@ -479,22 +457,23 @@ public static void slowTransitionNativeToVM() {
479457
int newStatus = StatusSupport.STATUS_IN_VM;
480458
boolean needSlowPath = !StatusSupport.compareAndSetNativeToNewStatus(newStatus);
481459
if (BranchProbabilityNode.probability(BranchProbabilityNode.VERY_SLOW_PATH_PROBABILITY, needSlowPath)) {
482-
callSlowPathNativeToNewStatus(Safepoint.ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS, newStatus, false);
460+
callSlowPathNativeToNewStatus(ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS, newStatus, false);
483461
}
484462
}
485463

486464
@Uninterruptible(reason = "Must not contain safepoint checks")
487465
public static void transitionVMToJava(boolean popFrameAnchor) {
488-
// We can directly change the thread status as no other thread will touch the status field
489-
// as long as we are in VM status.
466+
/* Change the thread status directly (other threads won't touch the status field). */
490467
StatusSupport.assertStatusVM();
491468
StatusSupport.setStatusJavaUnguarded();
492469
if (popFrameAnchor) {
493470
JavaFrameAnchors.popFrameAnchor();
494471
}
495-
boolean needSlowPath = ThreadingSupportImpl.needsNativeToJavaSlowpath();
472+
473+
/* Only execute pending actions but don't do a safepoint slowpath call. */
474+
boolean needSlowPath = ActionOnTransitionToJavaSupport.isActionPending();
496475
if (BranchProbabilityNode.probability(BranchProbabilityNode.VERY_SLOW_PATH_PROBABILITY, needSlowPath)) {
497-
callSlowPathSafepointCheck(Safepoint.ENTER_SLOW_PATH_TRANSITION_FROM_VM_TO_JAVA);
476+
callRunPendingActions(ENTER_SLOW_PATH_RUN_PENDING_ACTIONS);
498477
}
499478
}
500479

@@ -514,16 +493,11 @@ public static void transitionVMToNative() {
514493
StatusSupport.setStatusNative();
515494
}
516495

517-
@NodeIntrinsic(value = ForeignCallNode.class)
518-
private static native void callSlowPathSafepointCheck(@ConstantNodeParameter ForeignCallDescriptor descriptor);
519-
520496
/**
521497
* Block until I can transition from native to a new thread status. This is not inlined and need
522498
* not be fast. In fact, it often blocks. But it can not do much except block, since it starts
523499
* out running with "native" thread status.
524500
*
525-
* Foreign call: {@link #ENTER_SLOW_PATH_TRANSITION_FROM_NATIVE_TO_NEW_STATUS}.
526-
*
527501
* This method cannot use the {@link StubCallingConvention} with callee saved registers: the
528502
* reference map of the C call and this slow-path call must be the same. This is only guaranteed
529503
* when both the C call and the call to this slow path do not use callee saved registers.
@@ -543,27 +517,27 @@ private static void enterSlowPathTransitionFromNativeToNewStatus(int newStatus,
543517
}
544518
}
545519

546-
@NodeIntrinsic(value = ForeignCallNode.class)
547-
private static native void callSlowPathNativeToNewStatus(@ConstantNodeParameter ForeignCallDescriptor descriptor, int newThreadStatus, boolean popFrameAnchor);
548-
549520
/**
550-
* Transitions from VM to Java do not need a safepoint check. We only need to make sure that any
551-
* {@link ActionOnTransitionToJavaSupport pending transition action} is executed.
552-
*
553-
* Foreign call: {@link #ENTER_SLOW_PATH_TRANSITION_FROM_VM_TO_JAVA}.
521+
* Runs any {@link ActionOnTransitionToJavaSupport pending transition actions}.
554522
*
555523
* This method cannot use the {@link StubCallingConvention} with callee saved registers: the
556524
* reference map of the C call and this slow-path call must be the same. This is only guaranteed
557525
* when both the C call and the call to this slow path do not use callee saved registers.
558526
*/
559-
@SubstrateForeignCallTarget(stubCallingConvention = false)
527+
@SubstrateForeignCallTarget(stubCallingConvention = false, fullyUninterruptible = true)
560528
@Uninterruptible(reason = "Must not contain safepoint checks.")
561-
private static void enterSlowPathTransitionFromVMToJava() {
529+
private static void enterSlowPathRunPendingActions() {
562530
VMError.guarantee(StatusSupport.isStatusJava(), "Must be already back in Java mode");
563-
564-
slowPathRunJavaStateActions();
531+
assert ActionOnTransitionToJavaSupport.isActionPending() : "must not be called otherwise";
532+
ActionOnTransitionToJavaSupport.runPendingActions();
565533
}
566534

535+
@NodeIntrinsic(value = ForeignCallNode.class)
536+
private static native void callRunPendingActions(@ConstantNodeParameter ForeignCallDescriptor descriptor);
537+
538+
@NodeIntrinsic(value = ForeignCallNode.class)
539+
private static native void callSlowPathNativeToNewStatus(@ConstantNodeParameter ForeignCallDescriptor descriptor, int newThreadStatus, boolean popFrameAnchor);
540+
567541
/** Methods for the thread that brings the system to a safepoint. */
568542
@AutomaticallyRegisteredImageSingleton
569543
public static final class Master {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/thread/VMThreads.java

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
import com.oracle.svm.core.Uninterruptible;
4242
import com.oracle.svm.core.c.function.CEntryPointErrors;
4343
import com.oracle.svm.core.c.function.CFunctionOptions;
44+
import com.oracle.svm.core.config.ConfigurationValues;
4445
import com.oracle.svm.core.feature.AutomaticallyRegisteredFeature;
4546
import com.oracle.svm.core.feature.InternalFeature;
4647
import com.oracle.svm.core.heap.Heap;
@@ -55,6 +56,7 @@
5556
import com.oracle.svm.core.memory.UntrackedNullableNativeMemory;
5657
import com.oracle.svm.core.nodes.CFunctionEpilogueNode;
5758
import com.oracle.svm.core.nodes.CFunctionPrologueNode;
59+
import com.oracle.svm.core.nodes.CodeSynchronizationNode;
5860
import com.oracle.svm.core.threadlocal.FastThreadLocal;
5961
import com.oracle.svm.core.threadlocal.FastThreadLocalBytes;
6062
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
@@ -65,10 +67,12 @@
6567
import com.oracle.svm.core.util.VMError;
6668

6769
import jdk.graal.compiler.api.directives.GraalDirectives;
70+
import jdk.graal.compiler.api.replacements.Fold;
6871
import jdk.graal.compiler.core.common.SuppressFBWarnings;
6972
import jdk.graal.compiler.replacements.ReplacementsUtil;
7073
import jdk.graal.compiler.replacements.nodes.AssertionNode;
7174
import jdk.graal.compiler.word.Word;
75+
import jdk.vm.ci.aarch64.AArch64;
7276

7377
/**
7478
* Utility methods for the manipulation and iteration of {@link IsolateThread}s.
@@ -969,6 +973,7 @@ public static String toString(int safepointBehavior) {
969973

970974
/**
971975
* A thread-local enum conveying any actions needed before thread begins executing Java code.
976+
* Only used on aarch64 at the moment.
972977
*/
973978
public static class ActionOnTransitionToJavaSupport {
974979

@@ -982,26 +987,36 @@ public static class ActionOnTransitionToJavaSupport {
982987

983988
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
984989
public static boolean isActionPending() {
985-
return actionTL.getVolatile() != NO_ACTION;
990+
if (!isAarch64()) {
991+
return false;
992+
}
993+
return actionTL.get() != NO_ACTION;
986994
}
987995

988996
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
989-
public static boolean isSynchronizeCode() {
990-
return actionTL.getVolatile() == SYNCHRONIZE_CODE;
991-
}
997+
public static void runPendingActions() {
998+
if (!isAarch64() || !ActionOnTransitionToJavaSupport.isActionPending()) {
999+
return;
1000+
}
9921001

993-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
994-
public static void clearActions() {
995-
actionTL.setVolatile(NO_ACTION);
1002+
assert actionTL.get() == SYNCHRONIZE_CODE;
1003+
CodeSynchronizationNode.synchronizeCode();
1004+
actionTL.set(NO_ACTION);
9961005
}
9971006

9981007
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
9991008
public static void setSynchronizeCode(IsolateThread vmThread) {
1009+
if (!isAarch64()) {
1010+
return;
1011+
}
1012+
10001013
assert StatusSupport.isStatusCreated(vmThread) || VMOperation.isInProgressAtSafepoint() : "Invariant to avoid races between setting and clearing.";
1001-
actionTL.setVolatile(vmThread, SYNCHRONIZE_CODE);
1014+
actionTL.set(vmThread, SYNCHRONIZE_CODE);
10021015
}
10031016

10041017
public static void requestAllThreadsSynchronizeCode() {
1018+
assert isAarch64();
1019+
10051020
final IsolateThread myself = CurrentIsolate.getCurrentThread();
10061021
for (IsolateThread vmThread = VMThreads.firstThread(); vmThread.isNonNull(); vmThread = VMThreads.nextThread(vmThread)) {
10071022
if (myself == vmThread) {
@@ -1010,6 +1025,11 @@ public static void requestAllThreadsSynchronizeCode() {
10101025
setSynchronizeCode(vmThread);
10111026
}
10121027
}
1028+
1029+
@Fold
1030+
static boolean isAarch64() {
1031+
return ConfigurationValues.getTarget().arch instanceof AArch64;
1032+
}
10131033
}
10141034

10151035
public interface OSThreadHandle extends PointerBase {

0 commit comments

Comments
 (0)