Skip to content

Commit 1d966f7

Browse files
Simplify the safepoint handling and fix assertions.
1 parent e1721dc commit 1d966f7

File tree

4 files changed

+50
-52
lines changed

4 files changed

+50
-52
lines changed

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

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,13 @@ boolean isInProgress() {
116116
return safepointState == AT_SAFEPOINT;
117117
}
118118

119+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
120+
public boolean isPendingOrInProgress() {
121+
/* Only read the state once. */
122+
int state = safepointState;
123+
return state == SYNCHRONIZING || state == AT_SAFEPOINT;
124+
}
125+
119126
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
120127
public UnsignedWord getSafepointId() {
121128
return safepointId;
@@ -126,11 +133,6 @@ public boolean isMasterThread() {
126133
return requestingThread == CurrentIsolate.getCurrentThread();
127134
}
128135

129-
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
130-
public boolean isPendingOrInProgress() {
131-
return requestingThread.isNonNull();
132-
}
133-
134136
/**
135137
* Initiates a safepoint.
136138
*
@@ -150,12 +152,13 @@ boolean startSafepoint(String reason) {
150152
THREAD_MUTEX.lock();
151153
}
152154

155+
assert requestingThread.isNull();
153156
requestingThread = CurrentIsolate.getCurrentThread();
154157
ImageSingletons.lookup(Heap.class).prepareForSafepoint();
158+
155159
safepointState = SYNCHRONIZING;
160+
int numJavaThreads = requestThreadsEnterSafepoint(reason);
156161

157-
int numJavaThreads = requestThreadsEnterSafepoint();
158-
waitUntilThreadsEnterSafepoint(reason);
159162
safepointState = AT_SAFEPOINT;
160163
safepointId = safepointId.add(1);
161164
SafepointBeginEvent.emit(getSafepointId(), numJavaThreads, startTicks);
@@ -167,46 +170,34 @@ boolean startSafepoint(String reason) {
167170
void endSafepoint(boolean unlock) {
168171
assert VMOperationControl.mayExecuteVmOperations();
169172
long startTicks = JfrTicks.elapsedTicks();
173+
170174
safepointState = NOT_AT_SAFEPOINT;
171175
releaseThreadsFromSafepoint();
172176
/* Some Java threads can continue execution now. */
173177

174-
SafepointEndEvent.emit(getSafepointId(), startTicks);
175-
ImageSingletons.lookup(Heap.class).endSafepoint();
176-
177-
requestingThread = Word.nullPointer();
178178
if (unlock) {
179179
THREAD_MUTEX.unlock();
180180
}
181181

182-
/* This can take a moment, so we do it after letting all other threads proceed. */
183-
VMThreads.singleton().cleanupExitedOsThreads();
184-
}
185-
186-
/**
187-
* Ask all other threads to come to a safepoint. There is no guarantee that the threads will
188-
* comply with the request.
189-
*/
190-
private static int requestThreadsEnterSafepoint() {
191-
assert THREAD_MUTEX.isOwner() : "must hold mutex while requesting a safepoint";
192-
int numJavaThreads = 0;
182+
/*
183+
* Now that the safepoint was released, we can treat this thread like a normal VM thread and
184+
* don't need the special handling in the safepoint slowpath anymore. Other threads also
185+
* can't start a new safepoint yet (this code is still executed in the VM operation thread
186+
* and only the VM operation thread may start a safepoint).
187+
*/
188+
requestingThread = Word.nullPointer();
193189

194-
for (IsolateThread thread = VMThreads.firstThread(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) {
195-
numJavaThreads++;
196-
if (thread == CurrentIsolate.getCurrentThread()) {
197-
continue;
198-
}
199-
if (SafepointBehavior.ignoresSafepoints(thread)) {
200-
/* If safepoints are disabled, do not ask it to stop at safepoints. */
201-
continue;
202-
}
203-
requestEnterSafepoint(thread);
204-
}
205-
return numJavaThreads;
190+
/*
191+
* Everything down here can take a moment, so we do it after letting all other threads
192+
* proceed.
193+
*/
194+
ImageSingletons.lookup(Heap.class).endSafepoint();
195+
SafepointEndEvent.emit(getSafepointId(), startTicks);
196+
VMThreads.singleton().cleanupExitedOsThreads();
206197
}
207198

208-
/** Wait until all other threads reach a safepoint. Re-requests safepoints if necessary. */
209-
private static void waitUntilThreadsEnterSafepoint(String reason) {
199+
/** Blocks until all other threads entered the safepoint. */
200+
private static int requestThreadsEnterSafepoint(String reason) {
210201
assert THREAD_MUTEX.isOwner() : "must hold mutex while waiting for safepoints";
211202

212203
long startNanos = System.nanoTime();
@@ -216,12 +207,13 @@ private static void waitUntilThreadsEnterSafepoint(String reason) {
216207
long failureNanos = -1;
217208

218209
for (int loopCount = 1; /* return */ ; loopCount++) {
210+
int numThreads = 0;
219211
int atSafepoint = 0;
220212
int ignoreSafepoints = 0;
221213
int notAtSafepoint = 0;
222-
int lostUpdates = 0;
223214

224215
for (IsolateThread thread = VMThreads.firstThread(); thread.isNonNull(); thread = VMThreads.nextThread(thread)) {
216+
numThreads++;
225217
if (thread == CurrentIsolate.getCurrentThread()) {
226218
continue;
227219
}
@@ -245,10 +237,9 @@ private static void waitUntilThreadsEnterSafepoint(String reason) {
245237
switch (status) {
246238
case StatusSupport.STATUS_IN_JAVA:
247239
case StatusSupport.STATUS_IN_VM: {
248-
/* Re-request the safepoint in case of a lost update. */
240+
/* Request the safepoint (or re-request in case of a lost update). */
249241
if (SafepointCheckCounter.getVolatile(thread) > 0) {
250242
requestEnterSafepoint(thread);
251-
lostUpdates++;
252243
}
253244
notAtSafepoint++;
254245
break;
@@ -274,8 +265,8 @@ private static void waitUntilThreadsEnterSafepoint(String reason) {
274265
}
275266

276267
if (notAtSafepoint == 0) {
277-
/* All threads entered the safepoint. */
278-
return;
268+
/* All relevant threads entered the safepoint. */
269+
return numThreads;
279270
}
280271

281272
if (warningNanos == -1 || failureNanos == -1) {
@@ -297,7 +288,6 @@ private static void waitUntilThreadsEnterSafepoint(String reason) {
297288
.string(" atSafepoint: ").signed(atSafepoint)
298289
.string(" ignoreSafepoints: ").signed(ignoreSafepoints)
299290
.string(" notAtSafepoint: ").signed(notAtSafepoint)
300-
.string(" lostUpdates: ").signed(lostUpdates)
301291
.string("]")
302292
.newline();
303293

@@ -333,6 +323,7 @@ private static void releaseThreadsFromSafepoint() {
333323
continue;
334324
}
335325

326+
assert StatusSupport.getStatusVolatile(thread) == StatusSupport.STATUS_IN_SAFEPOINT;
336327
restoreCounter(thread);
337328

338329
/* Skip suspended threads so that they remain in STATUS_IN_SAFEPOINT. */

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,17 @@
3939
/**
4040
* Per-thread counter for safepoint checks. If the counter reaches a value less or equal 0, the
4141
* {@link SafepointSlowpath} is entered.
42-
*
42+
* <p>
4343
* Be careful when calling any of the methods that manipulate or set the counter value directly as
4444
* they have the potential to destroy or skew the data that is needed for scheduling the execution
4545
* of recurring callbacks (i.e., the number of executed safepoints in a period of time). See class
4646
* {@link RecurringCallbackSupport} for more details about recurring callbacks.
47-
*
48-
* The safepoint check counter can have one of the following values:
47+
* <p>
48+
* If the recurring callback support is disabled, the safepoint check counter can only be 0
49+
* (safepoint requested) or {@link #MAX_VALUE} (normal execution).
50+
* <p>
51+
* If the recurring callback support is enabled, the safepoint check counter can have one of the
52+
* following values:
4953
* <ul>
5054
* <li>value > 0: remaining number of safepoint checks before the safepoint slowpath code is
5155
* executed.</li>
@@ -69,7 +73,7 @@ static boolean compareAndSet(IsolateThread thread, int oldValue, int newValue) {
6973
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
7074
static void setVolatile(IsolateThread thread, int value) {
7175
assert CurrentIsolate.getCurrentThread() == thread || VMThreads.StatusSupport.isStatusCreated(thread) || VMOperationControl.mayExecuteVmOperations();
72-
assert value > 0;
76+
assert RecurringCallbackSupport.isEnabled() && value > 0 || !RecurringCallbackSupport.isEnabled() && (value == 0 || value == MAX_VALUE);
7377
valueTL.setVolatile(thread, value);
7478
}
7579

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import com.oracle.svm.core.snippets.SnippetRuntime.SubstrateForeignCallDescriptor;
3939
import com.oracle.svm.core.snippets.SubstrateForeignCallTarget;
4040
import com.oracle.svm.core.stack.JavaFrameAnchors;
41+
import com.oracle.svm.core.thread.RecurringCallbackSupport.RecurringCallbackTimer;
4142
import com.oracle.svm.core.thread.VMThreads.ActionOnTransitionToJavaSupport;
4243
import com.oracle.svm.core.thread.VMThreads.StatusSupport;
4344
import com.oracle.svm.core.util.VMError;
@@ -48,11 +49,11 @@
4849
* {@link #slowPathSafepointCheck(int, boolean, boolean) slowpath}. See {@link SafepointCheckNode}
4950
* and {@link SafepointSnippets} for more details.
5051
* <p>
51-
* {@link RecurringCallbackSupport} are implemented on top of the safepoint mechanism. If
52-
* {@linkplain RecurringCallbackSupport#isEnabled supported}, each safepoint check decrements
53-
* {@link SafepointCheckCounter} before the comparison. So, threads enter the safepoint slowpath
54-
* periodically, even if no safepoint is pending. At the end of the slowpath, the recurring callback
55-
* is executed if the timer has expired.
52+
* {@link RecurringCallbackSupport Recurring callbacks} are implemented on top of the safepoint
53+
* mechanism. If {@linkplain RecurringCallbackSupport#isEnabled supported}, each safepoint check
54+
* decrements {@link SafepointCheckCounter} before the comparison. So, threads enter the safepoint
55+
* slowpath periodically, even if no safepoint is pending. At the end of the slowpath, the recurring
56+
* callback is executed if the timer has expired.
5657
*/
5758
public class SafepointSlowpath {
5859
/*
@@ -134,7 +135,7 @@ private static void slowPathSafepointCheck(int newStatus, boolean callerHasJavaF
134135
slowPathSafepointCheck0(newStatus, callerHasJavaFrameAnchor, popFrameAnchor);
135136
} catch (RecurringCallbackSupport.SafepointException e) {
136137
/* This exception is intended to be thrown from safepoint checks, at one's own risk */
137-
throw RecurringCallbackSupport.RecurringCallbackTimer.getAndClearPendingException();
138+
throw RecurringCallbackTimer.getAndClearPendingException();
138139
} catch (Throwable ex) {
139140
/*
140141
* The foreign call from snippets to this method does not have an exception edge. So we

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
import jdk.graal.compiler.api.directives.GraalDirectives;
7070
import jdk.graal.compiler.api.replacements.Fold;
7171
import jdk.graal.compiler.core.common.SuppressFBWarnings;
72+
import jdk.graal.compiler.nodes.PauseNode;
7273
import jdk.graal.compiler.replacements.ReplacementsUtil;
7374
import jdk.graal.compiler.replacements.nodes.AssertionNode;
7475
import jdk.graal.compiler.word.Word;
@@ -214,6 +215,7 @@ public static boolean ensureInitialized() {
214215
/* Already initialized, or some other thread claimed the initialization lock. */
215216
while (initializationState.get() < STATE_INITIALIZED) {
216217
/* Busy wait until the other thread finishes the initialization. */
218+
PauseNode.pause();
217219
}
218220
}
219221
return result;

0 commit comments

Comments
 (0)