Skip to content

Commit 4b926c2

Browse files
[GR-35763] Fix a deadlock when Thread.join is used on the main thread.
PullRequest: graal/10670
2 parents 3fc931b + 7d262f0 commit 4b926c2

File tree

5 files changed

+74
-31
lines changed

5 files changed

+74
-31
lines changed

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

Lines changed: 69 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@
3333
import java.util.List;
3434

3535
import org.graalvm.compiler.word.Word;
36+
import org.graalvm.nativeimage.CurrentIsolate;
3637
import org.graalvm.nativeimage.ImageSingletons;
38+
import org.graalvm.nativeimage.Isolate;
3739
import org.graalvm.nativeimage.Platform;
3840
import org.graalvm.nativeimage.Platforms;
3941
import org.graalvm.nativeimage.VMRuntime;
@@ -47,20 +49,24 @@
4749
import org.graalvm.word.UnsignedWord;
4850
import org.graalvm.word.WordFactory;
4951

50-
import com.oracle.svm.core.annotate.AlwaysInline;
5152
import com.oracle.svm.core.annotate.Uninterruptible;
5253
import com.oracle.svm.core.c.CGlobalData;
5354
import com.oracle.svm.core.c.CGlobalDataFactory;
5455
import com.oracle.svm.core.c.function.CEntryPointActions;
5556
import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters;
5657
import com.oracle.svm.core.c.function.CEntryPointErrors;
5758
import com.oracle.svm.core.c.function.CEntryPointOptions;
59+
import com.oracle.svm.core.c.function.CEntryPointOptions.NoEpilogue;
60+
import com.oracle.svm.core.c.function.CEntryPointOptions.NoPrologue;
61+
import com.oracle.svm.core.c.function.CEntryPointSetup;
5862
import com.oracle.svm.core.jdk.InternalVMMethod;
5963
import com.oracle.svm.core.jdk.RuntimeSupport;
6064
import com.oracle.svm.core.log.Log;
6165
import com.oracle.svm.core.thread.JavaThreads;
6266
import com.oracle.svm.core.thread.PlatformThreads;
67+
import com.oracle.svm.core.thread.VMThreads;
6368
import com.oracle.svm.core.util.Counter;
69+
import com.oracle.svm.core.util.VMError;
6470

6571
import jdk.vm.ci.code.Architecture;
6672

@@ -119,17 +125,20 @@ public List<String> getInputArguments() {
119125
}
120126
}
121127

128+
@Uninterruptible(reason = "The caller initialized the thread state, so the callees do not need to be uninterruptible.", calleeMustBe = false)
129+
private static int runCore() {
130+
return runCore0();
131+
}
132+
122133
/**
123134
* Used by JavaMainWrapper and any user supplied main entry point (from
124135
* {@link org.graalvm.nativeimage.hosted.Feature.AfterRegistrationAccess}).
125136
*/
126-
@AlwaysInline(value = "Single callee from the main entry point.")
127-
public static int runCore() {
137+
private static int runCore0() {
128138
Architecture imageArchitecture = ImageSingletons.lookup(SubstrateTargetDescription.class).arch;
129139
CPUFeatureAccess cpuFeatureAccess = ImageSingletons.lookup(CPUFeatureAccess.class);
130140
cpuFeatureAccess.verifyHostSupportsArchitecture(imageArchitecture);
131141

132-
int exitCode;
133142
try {
134143
if (SubstrateOptions.ParseRuntimeOptions.getValue()) {
135144
/*
@@ -147,10 +156,7 @@ public static int runCore() {
147156
*/
148157
JavaMainSupport mainSupport = ImageSingletons.lookup(JavaMainSupport.class);
149158
mainSupport.javaMainHandle.invokeExact(mainSupport.mainArgs);
150-
151-
/* The application terminated normally. */
152-
exitCode = 0;
153-
159+
return 0;
154160
} catch (Throwable ex) {
155161
JavaThreads.dispatchUncaughtException(Thread.currentThread(), ex);
156162

@@ -159,30 +165,54 @@ public static int runCore() {
159165
* if an uncaught exception handler is registered. This behavior is the same on the Java
160166
* HotSpot VM.
161167
*/
162-
exitCode = 1;
163-
168+
return 1;
164169
} finally {
165-
/*
166-
* Shutdown sequence: First wait for all non-daemon threads to exit.
167-
*/
168-
PlatformThreads.singleton().joinAllNonDaemons();
169-
/*
170-
* Run shutdown hooks (both our own hooks and application-registered hooks. Note that
171-
* this can start new non-daemon threads. We are not responsible to wait until they have
172-
* exited.
173-
*/
174-
RuntimeSupport.getRuntimeSupport().shutdown();
175-
176-
Counter.logValues(Log.log());
170+
PlatformThreads.exit(Thread.currentThread());
177171
}
178-
return exitCode;
179172
}
180173

174+
@Uninterruptible(reason = "The caller initialized the thread state, so the callees do not need to be uninterruptible.", calleeMustBe = false)
175+
private static void runShutdown() {
176+
runShutdown0();
177+
}
178+
179+
private static void runShutdown0() {
180+
PlatformThreads.ensureCurrentAssigned("DestroyJavaVM", null, false);
181+
182+
// Shutdown sequence: First wait for all non-daemon threads to exit.
183+
PlatformThreads.singleton().joinAllNonDaemons();
184+
185+
/*
186+
* Run shutdown hooks (both our own hooks and application-registered hooks. Note that this
187+
* can start new non-daemon threads. We are not responsible to wait until they have exited.
188+
*/
189+
RuntimeSupport.getRuntimeSupport().shutdown();
190+
191+
Counter.logValues(Log.log());
192+
}
193+
194+
@Uninterruptible(reason = "Thread state not set up yet.")
181195
@CEntryPoint(include = CEntryPoint.NotIncludedAutomatically.class)
182-
@CEntryPointOptions(prologue = EnterCreateIsolateWithCArgumentsPrologue.class)
183-
@SuppressWarnings("unused")
196+
@CEntryPointOptions(prologue = NoPrologue.class, epilogue = NoEpilogue.class)
184197
public static int run(int argc, CCharPointerPointer argv) {
185-
return runCore();
198+
try {
199+
// Create the isolate and attach the current C thread as the main Java thread.
200+
EnterCreateIsolateWithCArgumentsPrologue.enter(argc, argv);
201+
assert !VMThreads.wasStartedByCurrentIsolate(CurrentIsolate.getCurrentThread()) : "re-attach would cause issues otherwise";
202+
203+
Isolate isolate = CurrentIsolate.getIsolate();
204+
int exitCode = runCore();
205+
CEntryPointSetup.LeaveDetachThreadEpilogue.leave();
206+
207+
// Re-attach the same C thread as another Java thread.
208+
EnterAttachThreadForShutdown.enter(isolate);
209+
runShutdown();
210+
CEntryPointSetup.LeaveDetachThreadEpilogue.leave();
211+
212+
return exitCode;
213+
} catch (Throwable e) {
214+
throw VMError.shouldNotReachHere(e);
215+
}
186216
}
187217

188218
private static boolean isArgumentBlockSupported() {
@@ -283,4 +313,17 @@ public static void enter(int paramArgc, CCharPointerPointer paramArgv) {
283313
}
284314
}
285315
}
316+
317+
public static class EnterAttachThreadForShutdown implements CEntryPointOptions.Prologue {
318+
private static final CGlobalData<CCharPointer> errorMessage = CGlobalDataFactory.createCString(
319+
"Failed to re-attach the main thread for shutting down the main isolate.");
320+
321+
@Uninterruptible(reason = "prologue")
322+
static void enter(Isolate isolate) {
323+
int code = CEntryPointActions.enterAttachThread(isolate, false);
324+
if (code != CEntryPointErrors.NO_ERROR) {
325+
CEntryPointActions.failFatally(code, errorMessage.get());
326+
}
327+
}
328+
}
286329
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@
5151
import com.oracle.svm.core.c.function.CEntryPointActions;
5252
import com.oracle.svm.core.c.function.CEntryPointErrors;
5353
import com.oracle.svm.core.graal.nodes.WriteCurrentVMThreadNode;
54-
import com.oracle.svm.core.graal.nodes.WriteHeapBaseNode;
54+
import com.oracle.svm.core.graal.snippets.CEntryPointSnippets;
5555
import com.oracle.svm.core.jdk.RuntimeSupport;
5656
import com.oracle.svm.core.log.Log;
5757
import com.oracle.svm.core.option.RuntimeOptionKey;
@@ -132,7 +132,7 @@ protected static boolean tryEnterIsolate(RegisterDumper.Context context) {
132132
// the crash happened in native code that was linked into Native Image.
133133
if (SubstrateOptions.SpawnIsolates.getValue()) {
134134
PointerBase heapBase = RegisterDumper.singleton().getHeapBase(context);
135-
WriteHeapBaseNode.writeCurrentVMHeapBase(heapBase);
135+
CEntryPointSnippets.setHeapBase(heapBase);
136136
}
137137
if (SubstrateOptions.MultiThreaded.getValue()) {
138138
PointerBase threadPointer = RegisterDumper.singleton().getThreadPointer(context);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ public static final class LeaveDetachThreadEpilogue implements CEntryPointOption
108108
"Failed to leave the current IsolateThread context and to detach the current thread.");
109109

110110
@Uninterruptible(reason = "epilogue")
111-
static void leave() {
111+
public static void leave() {
112112
int code = CEntryPointActions.leaveDetachThread();
113113
if (code != 0) {
114114
CEntryPointActions.failFatally(code, errorMessage.get());

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ private static boolean isApplicationThread(IsolateThread isolateThread) {
599599
}
600600

601601
@SuppressFBWarnings(value = "NN", justification = "notifyAll is necessary for Java semantics, no shared state needs to be modified beforehand")
602-
protected static void exit(Thread thread) {
602+
public static void exit(Thread thread) {
603603
/*
604604
* First call Thread.exit(). This allows waiters on the thread object to observe that a
605605
* daemon ThreadGroup is destroyed as well if this thread happens to be the last thread of a

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -369,7 +369,7 @@ public void detachThread(IsolateThread thread) {
369369
}
370370

371371
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
372-
private static boolean wasStartedByCurrentIsolate(IsolateThread thread) {
372+
public static boolean wasStartedByCurrentIsolate(IsolateThread thread) {
373373
return StartedByCurrentIsolate.getAddress(thread).readByte(0) != 0;
374374
}
375375

0 commit comments

Comments
 (0)