Skip to content

Commit 1e26b5f

Browse files
Make the exception handling fully uninterruptible.
1 parent 8f8e0c1 commit 1e26b5f

File tree

3 files changed

+29
-48
lines changed

3 files changed

+29
-48
lines changed

substratevm/src/com.oracle.svm.core.graal.llvm/src/com/oracle/svm/core/graal/llvm/runtime/LLVMExceptionUnwind.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
package com.oracle.svm.core.graal.llvm.runtime;
2626

2727
import static com.oracle.svm.core.util.VMError.shouldNotReachHere;
28+
import static org.graalvm.nativeimage.c.function.CFunction.Transition.NO_TRANSITION;
2829

2930
import java.util.function.BooleanSupplier;
3031

31-
import jdk.graal.compiler.core.common.NumUtil;
3232
import org.graalvm.nativeimage.CurrentIsolate;
3333
import org.graalvm.nativeimage.IsolateThread;
3434
import org.graalvm.nativeimage.c.CContext;
@@ -50,6 +50,7 @@
5050
import com.oracle.svm.core.stack.StackOverflowCheck;
5151
import com.oracle.svm.hosted.code.CEntryPointCallStubSupport;
5252

53+
import jdk.graal.compiler.core.common.NumUtil;
5354
import jdk.vm.ci.meta.MetaAccessProvider;
5455
import jdk.vm.ci.meta.ResolvedJavaMethod;
5556

@@ -142,6 +143,7 @@ public static ResolvedJavaMethod getRetrieveExceptionMethod(MetaAccessProvider m
142143
public static ExceptionUnwind createRaiseExceptionHandler() {
143144
return new ExceptionUnwind() {
144145
@Override
146+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
145147
protected void customUnwindException(Pointer callerSP) {
146148
_Unwind_Exception exceptionStructure = UnsafeStackValue.get(_Unwind_Exception.class);
147149
exceptionStructure.set_exception_class(CurrentIsolate.getCurrentThread());
@@ -198,7 +200,7 @@ private interface _Unwind_Exception extends PointerBase {
198200
private interface _Unwind_Context extends PointerBase {
199201
}
200202

201-
@CFunction(value = "_Unwind_RaiseException")
203+
@CFunction(value = "_Unwind_RaiseException", transition = NO_TRANSITION)
202204
public static native int raiseException(_Unwind_Exception exception);
203205

204206
@CFunction(value = "_Unwind_GetIP")

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/deopt/DeoptimizedFrame.java

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,14 @@
2424
*/
2525
package com.oracle.svm.core.deopt;
2626

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

29-
import jdk.graal.compiler.word.Word;
3031
import org.graalvm.nativeimage.PinnedObject;
3132
import org.graalvm.nativeimage.c.function.CodePointer;
3233
import org.graalvm.word.Pointer;
3334

34-
import com.oracle.svm.core.NeverInline;
3535
import com.oracle.svm.core.Uninterruptible;
3636
import com.oracle.svm.core.code.CodeInfo;
3737
import com.oracle.svm.core.code.CodeInfoAccess;
@@ -42,17 +42,15 @@
4242
import com.oracle.svm.core.config.ConfigurationValues;
4343
import com.oracle.svm.core.deopt.Deoptimizer.TargetContent;
4444
import com.oracle.svm.core.graal.stackvalue.UnsafeStackValue;
45-
import com.oracle.svm.core.heap.RestrictHeapAccess;
4645
import com.oracle.svm.core.log.StringBuilderLog;
4746
import com.oracle.svm.core.meta.SubstrateObjectConstant;
4847
import com.oracle.svm.core.monitor.MonitorSupport;
4948

5049
import jdk.graal.compiler.nodes.FrameState;
50+
import jdk.graal.compiler.word.Word;
5151
import jdk.vm.ci.code.InstalledCode;
5252
import jdk.vm.ci.meta.JavaConstant;
5353

54-
import static com.oracle.svm.core.Uninterruptible.CALLED_FROM_UNINTERRUPTIBLE_CODE;
55-
5654
/**
5755
* The handle to a deoptimized frame. It contains all stack entries which are written to the frame
5856
* of the deopt target method(s). For details see {@link Deoptimizer}.
@@ -407,6 +405,7 @@ protected void buildContent(Pointer newSp) {
407405
* deoptimization stub return to the exception handler instead of the regular return address of
408406
* the deoptimization target.
409407
*/
408+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
410409
public void takeException() {
411410
if (rethrowException) {
412411
/*
@@ -415,6 +414,7 @@ public void takeException() {
415414
*/
416415
return;
417416
}
417+
418418
ReturnAddress firstAddressEntry = topFrame.returnAddress;
419419
CodePointer ip = Word.pointer(firstAddressEntry.returnAddress);
420420
CodeInfo info = CodeInfoTable.getImageCodeInfo(ip);
@@ -427,13 +427,15 @@ public void takeException() {
427427
firstAddressEntry.returnAddress += handler;
428428
}
429429

430-
@NeverInline("Has more relaxed heap access requirements than caller.")
431-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.UNRESTRICTED, reason = "Printing out error and then crashing.")
430+
@Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
432431
private static void throwMissingExceptionHandler(CodeInfo info, ReturnAddress firstAddressEntry) {
432+
throwMissingExceptionHandler0(info, firstAddressEntry);
433+
}
434+
435+
private static void throwMissingExceptionHandler0(CodeInfo info, ReturnAddress firstAddressEntry) {
433436
CodeInfoQueryResult detailedQueryResult = new CodeInfoQueryResult();
434437
CodeInfoAccess.lookupCodeInfo(info, Word.pointer(firstAddressEntry.returnAddress), detailedQueryResult);
435438
FrameInfoQueryResult frameInfo = detailedQueryResult.getFrameInfo();
436439
throw Deoptimizer.fatalDeoptimizationError("No exception handler registered for deopt target", frameInfo);
437440
}
438-
439441
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/snippets/ExceptionUnwind.java

Lines changed: 15 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,12 @@
4545
import com.oracle.svm.core.deopt.DeoptimizationSupport;
4646
import com.oracle.svm.core.deopt.DeoptimizedFrame;
4747
import com.oracle.svm.core.deopt.Deoptimizer;
48-
import com.oracle.svm.core.heap.RestrictHeapAccess;
4948
import com.oracle.svm.core.log.Log;
5049
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
5150
import com.oracle.svm.core.stack.JavaFrame;
5251
import com.oracle.svm.core.stack.JavaFrames;
5352
import com.oracle.svm.core.stack.JavaStackWalk;
5453
import com.oracle.svm.core.stack.JavaStackWalker;
55-
import com.oracle.svm.core.stack.StackOverflowCheck;
5654
import com.oracle.svm.core.thread.VMThreads;
5755
import com.oracle.svm.core.threadlocal.FastThreadLocalBytes;
5856
import com.oracle.svm.core.threadlocal.FastThreadLocalFactory;
@@ -100,43 +98,25 @@ static boolean exceptionsAreFatal() {
10098

10199
/** Foreign call: {@link #UNWIND_EXCEPTION_WITHOUT_CALLEE_SAVED_REGISTERS}. */
102100
@SubstrateForeignCallTarget(stubCallingConvention = true)
103-
@Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
104-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
101+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
105102
private static void unwindExceptionWithoutCalleeSavedRegisters(Throwable exception, Pointer callerSP) {
106-
/*
107-
* Make the yellow zone available and pause recurring callbacks to avoid that unexpected
108-
* exceptions are thrown. This is reverted before execution continues in the exception
109-
* handler (see ExceptionStackFrameVisitor.visitFrame).
110-
*/
111-
StackOverflowCheck.singleton().makeYellowZoneAvailable();
112-
113-
unwindExceptionInterruptible(exception, callerSP, false, false);
103+
unwindException(exception, callerSP, false, false);
114104
}
115105

116106
/** Foreign call: {@link #UNWIND_EXCEPTION_WITH_CALLEE_SAVED_REGISTERS}. */
117107
@SubstrateForeignCallTarget(stubCallingConvention = true)
118-
@Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
119-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
108+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
120109
private static void unwindExceptionWithCalleeSavedRegisters(Throwable exception, Pointer callerSP) {
121-
StackOverflowCheck.singleton().makeYellowZoneAvailable();
122-
123-
unwindExceptionInterruptible(exception, callerSP, true, false);
110+
unwindException(exception, callerSP, true, false);
124111
}
125112

126-
@Uninterruptible(reason = "Must not execute recurring callbacks or a stack overflow check.", calleeMustBe = false)
127-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
113+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
128114
public static void unwindExceptionSkippingCaller(Throwable exception, Pointer callerSP) {
129-
StackOverflowCheck.singleton().makeYellowZoneAvailable();
130-
131-
unwindExceptionInterruptible(exception, callerSP, true, true);
115+
unwindException(exception, callerSP, true, true);
132116
}
133117

134-
/*
135-
* The stack walking objects must be stateless (no instance fields), because multiple threads
136-
* can use them simultaneously. All state must be in separate VMThreadLocals.
137-
*/
138-
@RestrictHeapAccess(access = RestrictHeapAccess.Access.NO_ALLOCATION, reason = "Must not allocate when unwinding the stack.")
139-
private static void unwindExceptionInterruptible(Throwable exception, Pointer callerSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
118+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
119+
private static void unwindException(Throwable exception, Pointer callerSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
140120
if (currentException.get() != null) {
141121
reportRecursiveUnwind(exception);
142122
return; /* Unreachable code. */
@@ -168,6 +148,7 @@ private static void unwindExceptionInterruptible(Throwable exception, Pointer ca
168148
* Exception unwinding cannot be called recursively. The most likely reason to end up here is an
169149
* exception being thrown while walking the stack to find an exception handler.
170150
*/
151+
@Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
171152
private static void reportRecursiveUnwind(Throwable exception) {
172153
Log.log().string("Fatal error: recursion in exception handling: ").string(exception.getClass().getName());
173154
Log.log().string(" thrown while unwinding ").string(currentException.get().getClass().getName()).newline().newline();
@@ -182,6 +163,7 @@ private static void reportRecursiveUnwind(Throwable exception) {
182163
* exception checks such as null pointer or array bounds checks. In such cases, exceptions are
183164
* treated as fatal errors.
184165
*/
166+
@Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
185167
private static void reportFatalUnwind(Throwable exception) {
186168
Log.log().string("Fatal error: exception unwind while thread is not in Java state: ");
187169
Log.log().exception(exception).newline().newline();
@@ -194,13 +176,15 @@ private static void reportFatalUnwind(Throwable exception) {
194176
* proper exception handling and reporting of "unhandled" user exceptions is at a higher level
195177
* using a normal Java catch-all exception handler.
196178
*/
179+
@Uninterruptible(reason = "Does not need to be uninterruptible because it throws a fatal error.", calleeMustBe = false)
197180
private static void reportUnhandledException(Throwable exception) {
198181
Log.log().string("Fatal error: unhandled exception in isolate ").hex(CurrentIsolate.getIsolate()).string(": ");
199182
Log.log().exception(exception).newline().newline();
200183
VMError.shouldNotReachHere("Unhandled exception");
201184
}
202185

203186
/** Hook to allow a {@link Feature} to install custom exception unwind code. */
187+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
204188
protected abstract void customUnwindException(Pointer callerSP);
205189

206190
/**
@@ -212,7 +196,7 @@ private static void reportUnhandledException(Throwable exception) {
212196
* @param skipCaller Whether the first (caller) frame should be skipped. If this is true, then
213197
* the value of fromMethodWithCalleeSavedRegisters will be ignored.
214198
*/
215-
@Uninterruptible(reason = "Prevent deoptimization apart from the few places explicitly considered safe for deoptimization")
199+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
216200
private static void defaultUnwindException(Pointer startSP, boolean fromMethodWithCalleeSavedRegisters, boolean skipCaller) {
217201
IsolateThread thread = CurrentIsolate.getCurrentThread();
218202
boolean hasCalleeSavedRegisters = fromMethodWithCalleeSavedRegisters;
@@ -236,7 +220,7 @@ private static void defaultUnwindException(Pointer startSP, boolean fromMethodWi
236220
DeoptimizedFrame deoptFrame = Deoptimizer.checkEagerDeoptimized(frame);
237221
if (deoptFrame != null) {
238222
/* Deoptimization entry points always have an exception handler. */
239-
deoptTakeExceptionInterruptible(deoptFrame);
223+
deoptFrame.takeException();
240224
jumpToHandler(sp, DeoptimizationSupport.getEagerDeoptStubPointer(), hasCalleeSavedRegisters);
241225
UnreachableNode.unreachable();
242226
return; /* Unreachable */
@@ -273,15 +257,13 @@ private static void defaultUnwindException(Pointer startSP, boolean fromMethodWi
273257
}
274258
}
275259

276-
@Uninterruptible(reason = "Prevent deoptimization while dispatching to exception handler")
260+
@Uninterruptible(reason = "Code that is fully uninterruptible may throw and catch exceptions. Therefore, the exception handling must be fully uninterruptible as well.")
277261
private static void jumpToHandler(Pointer sp, CodePointer handlerIP, boolean hasCalleeSavedRegisters) {
278262
verifyTopFrameAnchor(sp);
279263

280264
Throwable exception = currentException.get();
281265
currentException.set(null);
282266

283-
StackOverflowCheck.singleton().protectYellowZone();
284-
285267
if (hasCalleeSavedRegisters) {
286268
/*
287269
* The fromMethodWithCalleeSavedRegisters parameter of farReturn must be a compile-time
@@ -294,9 +276,4 @@ private static void jumpToHandler(Pointer sp, CodePointer handlerIP, boolean has
294276
}
295277
/* Unreachable code: the intrinsic performs a jump to the specified instruction pointer. */
296278
}
297-
298-
@Uninterruptible(reason = "Wrap call to interruptible code.", calleeMustBe = false)
299-
private static void deoptTakeExceptionInterruptible(DeoptimizedFrame deoptFrame) {
300-
deoptFrame.takeException();
301-
}
302279
}

0 commit comments

Comments
 (0)