Skip to content

Commit 177f485

Browse files
committed
Save and restore the state of stack overflow checks for continuations.
1 parent 78ebf7b commit 177f485

File tree

3 files changed

+62
-16
lines changed

3 files changed

+62
-16
lines changed

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

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -140,23 +140,44 @@ public void initialize(IsolateThread thread) {
140140
yellowZoneStateTL.set(thread, STATE_YELLOW_ENABLED);
141141
}
142142

143+
@Override
144+
public int getState() {
145+
return yellowZoneStateTL.get();
146+
}
147+
148+
@Override
149+
@Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.")
150+
public void setState(int newState) {
151+
int oldState = yellowZoneStateTL.get();
152+
yellowZoneStateTL.set(newState);
153+
154+
if (newState > oldState) {
155+
onYellowZoneMadeAvailable(oldState, newState);
156+
} else if (newState < oldState) {
157+
onYellowZoneProtected(oldState, newState);
158+
}
159+
}
160+
143161
@Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.")
144162
@Override
145163
public void makeYellowZoneAvailable() {
146-
/*
147-
* Even though "yellow zones" and "recurring callbacks" are orthogonal features, running a
148-
* recurring callback in the yellow zone is dangerous because a stack overflow in the
149-
* recurring callback would then lead to a fatal error.
150-
*/
151-
ThreadingSupportImpl.pauseRecurringCallback("Recurring callbacks are considered user code and must not run in yellow zone");
164+
setState(yellowZoneStateTL.get() + 1);
165+
}
152166

153-
int state = yellowZoneStateTL.get();
154-
VMError.guarantee(state >= STATE_YELLOW_ENABLED, "StackOverflowSupport.disableYellowZone: Illegal state");
167+
@Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.")
168+
private static void onYellowZoneMadeAvailable(int oldState, int newState) {
169+
VMError.guarantee(newState > oldState && newState > STATE_YELLOW_ENABLED, "StackOverflowCheckImpl.onYellowZoneMadeAvailable: Illegal state");
170+
171+
if (oldState == STATE_YELLOW_ENABLED) {
172+
/*
173+
* Even though "yellow zones" and "recurring callbacks" are orthogonal features, running
174+
* a recurring callback in the yellow zone is dangerous because a stack overflow in the
175+
* recurring callback would then lead to a fatal error.
176+
*/
177+
ThreadingSupportImpl.pauseRecurringCallback("Recurring callbacks are considered user code and must not run in yellow zone");
155178

156-
if (state == STATE_YELLOW_ENABLED) {
157179
stackBoundaryTL.set(stackBoundaryTL.get().subtract(Options.StackYellowZoneSize.getValue()));
158180
}
159-
yellowZoneStateTL.set(state + 1);
160181

161182
/*
162183
* Check that after enabling the yellow zone there is actually stack space available again.
@@ -180,14 +201,16 @@ public boolean isYellowZoneAvailable() {
180201
@Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.")
181202
@Override
182203
public void protectYellowZone() {
183-
ThreadingSupportImpl.resumeRecurringCallbackAtNextSafepoint();
204+
setState(yellowZoneStateTL.get() - 1);
205+
}
184206

185-
int state = yellowZoneStateTL.get();
186-
VMError.guarantee(state > STATE_YELLOW_ENABLED, "StackOverflowSupport.enableYellowZone: Illegal state");
207+
@Uninterruptible(reason = "Atomically manipulating state of multiple thread local variables.")
208+
private static void onYellowZoneProtected(int oldState, int newState) {
209+
VMError.guarantee(newState < oldState && newState >= STATE_YELLOW_ENABLED, "StackOverflowCheckImpl.onYellowZoneProtected: Illegal state");
187210

188-
int newState = state - 1;
189-
yellowZoneStateTL.set(newState);
190211
if (newState == STATE_YELLOW_ENABLED) {
212+
ThreadingSupportImpl.resumeRecurringCallbackAtNextSafepoint();
213+
191214
stackBoundaryTL.set(stackBoundaryTL.get().add(Options.StackYellowZoneSize.getValue()));
192215
}
193216
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/stack/StackOverflowCheck.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,15 @@ static StackOverflowCheck singleton() {
145145
*/
146146
int yellowAndRedZoneSize();
147147

148+
/** @see #setState */
149+
int getState();
150+
151+
/**
152+
* Restore the specified state of the stack overflow checks obtained from {@link #getState}.
153+
* This is intended for yielding and resuming continuations on a thread.
154+
*/
155+
void setState(int state);
156+
148157
/**
149158
* Disables all stack overflow checks for this thread. This operation is not reversible, i.e.,
150159
* it must only be called in the case of a fatal error where the VM is going to exit soon and

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import com.oracle.svm.core.heap.StoredContinuation;
3838
import com.oracle.svm.core.heap.StoredContinuationImpl;
3939
import com.oracle.svm.core.snippets.KnownIntrinsics;
40+
import com.oracle.svm.core.stack.StackOverflowCheck;
4041
import com.oracle.svm.core.util.VMError;
4142

4243
/**
@@ -65,6 +66,7 @@ public static boolean isSupported() {
6566
Pointer sp;
6667
CodePointer ip;
6768
private boolean done;
69+
private int overflowCheckState;
6870

6971
Continuation(Runnable target) {
7072
this.target = target;
@@ -75,7 +77,19 @@ public void setIP(CodePointer ip) {
7577
}
7678

7779
void enter() {
78-
enter0(ip.isNonNull());
80+
int stateBefore = StackOverflowCheck.singleton().getState();
81+
VMError.guarantee(!StackOverflowCheck.singleton().isYellowZoneAvailable(), "Stack overflow checks must be active when entering a continuation");
82+
83+
boolean isContinue = ip.isNonNull();
84+
if (isContinue) {
85+
StackOverflowCheck.singleton().setState(overflowCheckState);
86+
}
87+
try {
88+
enter0(isContinue);
89+
} finally {
90+
overflowCheckState = StackOverflowCheck.singleton().getState();
91+
StackOverflowCheck.singleton().setState(stateBefore);
92+
}
7993
}
8094

8195
@NeverInline("access stack pointer")

0 commit comments

Comments
 (0)