Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[GR-51512] [GR-51620] Simplify thread detach, isolate teardown, and fix a Windows-specific thread handle leak. #8304

Merged
merged 1 commit into from
Feb 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,15 @@

import java.util.Arrays;

import jdk.graal.compiler.api.replacements.Fold;
import org.graalvm.nativeimage.ImageSingletons;
import org.graalvm.nativeimage.Isolate;
import org.graalvm.nativeimage.Platform;
import org.graalvm.nativeimage.Platforms;

import com.oracle.svm.core.feature.AutomaticallyRegisteredImageSingleton;
import com.oracle.svm.core.util.VMError;

import jdk.graal.compiler.api.replacements.Fold;

@AutomaticallyRegisteredImageSingleton
public class IsolateListenerSupport {
Expand All @@ -58,22 +60,42 @@ public static IsolateListenerSupport singleton() {

@Uninterruptible(reason = "Thread state not yet set up.")
public void afterCreateIsolate(Isolate isolate) {
for (int i = 0; i < listeners.length; i++) {
listeners[i].afterCreateIsolate(isolate);
for (IsolateListener listener : listeners) {
try {
listener.afterCreateIsolate(isolate);
} catch (Throwable e) {
throw VMError.shouldNotReachHere(e);
}
}
}

@Uninterruptible(reason = "The isolate teardown is in progress.")
public void onIsolateTeardown() {
for (int i = 0; i < listeners.length; i++) {
listeners[i].onIsolateTeardown();
for (int i = listeners.length - 1; i >= 0; i--) {
try {
listeners[i].onIsolateTeardown();
} catch (Throwable e) {
throw VMError.shouldNotReachHere(e);
}
}
}

public interface IsolateListener {
/**
* Implementations must not throw any exceptions. Note that the thread that creates the
* isolate is still unattached when this method is called.
*/
@Uninterruptible(reason = "Thread state not yet set up.")
void afterCreateIsolate(Isolate isolate);

/**
* Implementations must not throw any exceptions. Note that this method is called on
* listeners in the reverse order of {@link #afterCreateIsolate}.
*
* This method is called during isolate teardown, when the VM is guaranteed to be
* single-threaded (i.e., all other threads already exited on the OS-level). This method is
* not called for applications that use {@link JavaMainWrapper}.
*/
@Uninterruptible(reason = "The isolate teardown is in progress.")
default void onIsolateTeardown() {
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,9 @@

import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters;
import com.oracle.svm.core.c.function.CEntryPointErrors;
import com.oracle.svm.core.c.function.CEntryPointSetup;
import com.oracle.svm.core.code.CodeInfoTable;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.util.VMError;

Expand Down Expand Up @@ -173,17 +170,4 @@ public static PointerBase getHeapBase(Isolate isolate) {
}
return isolate;
}

@Uninterruptible(reason = "Tear-down in progress.")
public static int tearDownCurrent() {
freeUnmanagedMemory();
Heap.getHeap().tearDown();
return CommittedMemoryProvider.get().tearDown();
}

@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
private static void freeUnmanagedMemory() {
CodeInfoTable.tearDown();
NonmovableArrays.tearDown();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,10 @@
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.jni.JNIJavaVMList;
import com.oracle.svm.core.jni.functions.JNIFunctionTables;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.thread.JavaThreads;
import com.oracle.svm.core.thread.PlatformThreads;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.thread.VMThreads.OSThreadHandle;
import com.oracle.svm.core.util.CounterSupport;
import com.oracle.svm.core.util.UserError;
import com.oracle.svm.core.util.VMError;
import com.oracle.svm.util.ClassUtil;
Expand Down Expand Up @@ -256,12 +254,12 @@ private static void runShutdown0() {
PlatformThreads.singleton().joinAllNonDaemons();

/*
* Run shutdown hooks (both our own hooks and application-registered hooks. Note that this
* can start new non-daemon threads. We are not responsible to wait until they have exited.
* Run shutdown hooks (both our own hooks and application-registered hooks) and teardown
* hooks. Note that this can start new non-daemon threads. We are not responsible to wait
* until they have exited.
*/
RuntimeSupport.getRuntimeSupport().shutdown();

CounterSupport.singleton().logValues(Log.log());
RuntimeSupport.executeTearDownHooks();
}

@Uninterruptible(reason = "Thread state not set up yet.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1270,7 +1270,9 @@ public static synchronized DiagnosticThunkRegistry singleton() {
if (ImageSingletons.contains(JavaMainWrapper.JavaMainSupport.class)) {
thunks.add(new DumpCommandLine());
}
thunks.add(new DumpCounters());
if (CounterSupport.isEnabled()) {
thunks.add(new DumpCounters());
}
if (RuntimeCompilation.isEnabled()) {
thunks.add(new DumpCodeCacheHistory());
thunks.add(new DumpRuntimeCodeInfoMemory());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import com.oracle.svm.core.Uninterruptible;
import com.oracle.svm.core.c.CGlobalData;
import com.oracle.svm.core.c.CGlobalDataFactory;
import com.oracle.svm.core.c.NonmovableArrays;
import com.oracle.svm.core.c.function.CEntryPointActions;
import com.oracle.svm.core.c.function.CEntryPointCreateIsolateParameters;
import com.oracle.svm.core.c.function.CEntryPointErrors;
Expand All @@ -69,7 +70,7 @@
import com.oracle.svm.core.graal.nodes.CEntryPointEnterNode;
import com.oracle.svm.core.graal.nodes.CEntryPointLeaveNode;
import com.oracle.svm.core.graal.nodes.CEntryPointUtilityNode;
import com.oracle.svm.core.graal.nodes.WriteCurrentVMThreadNode;
import com.oracle.svm.core.heap.Heap;
import com.oracle.svm.core.heap.PhysicalMemory;
import com.oracle.svm.core.heap.ReferenceHandler;
import com.oracle.svm.core.heap.ReferenceHandlerThread;
Expand All @@ -78,6 +79,7 @@
import com.oracle.svm.core.jdk.RuntimeSupport;
import com.oracle.svm.core.log.Log;
import com.oracle.svm.core.option.RuntimeOptionParser;
import com.oracle.svm.core.os.CommittedMemoryProvider;
import com.oracle.svm.core.os.MemoryProtectionProvider;
import com.oracle.svm.core.os.VirtualMemoryProvider;
import com.oracle.svm.core.snippets.SnippetRuntime;
Expand All @@ -87,6 +89,7 @@
import com.oracle.svm.core.thread.PlatformThreads;
import com.oracle.svm.core.thread.Safepoint;
import com.oracle.svm.core.thread.ThreadListenerSupport;
import com.oracle.svm.core.thread.ThreadingSupportImpl;
import com.oracle.svm.core.thread.VMOperationControl;
import com.oracle.svm.core.thread.VMThreads;
import com.oracle.svm.core.thread.VMThreads.SafepointBehavior;
Expand Down Expand Up @@ -134,7 +137,7 @@ public final class CEntryPointSnippets extends SubstrateTemplates implements Sni
public static final SubstrateForeignCallDescriptor ENTER_BY_ISOLATE = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "enterByIsolate",
HAS_SIDE_EFFECT, LocationIdentity.any());

public static final SubstrateForeignCallDescriptor DETACH_THREAD = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "detachThread",
public static final SubstrateForeignCallDescriptor DETACH_CURRENT_THREAD = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "detachCurrentThread",
HAS_SIDE_EFFECT, LocationIdentity.any());

public static final SubstrateForeignCallDescriptor REPORT_EXCEPTION = SnippetRuntime.findForeignCall(CEntryPointSnippets.class, "reportException",
Expand All @@ -147,7 +150,7 @@ public final class CEntryPointSnippets extends SubstrateTemplates implements Sni
LocationIdentity.any());

public static final SubstrateForeignCallDescriptor[] FOREIGN_CALLS = {CREATE_ISOLATE, INITIALIZE_ISOLATE, ATTACH_THREAD, ENSURE_JAVA_THREAD, ENTER_BY_ISOLATE,
DETACH_THREAD, REPORT_EXCEPTION, TEAR_DOWN_ISOLATE, IS_ATTACHED, FAIL_FATALLY, VERIFY_ISOLATE_THREAD};
DETACH_CURRENT_THREAD, REPORT_EXCEPTION, TEAR_DOWN_ISOLATE, IS_ATTACHED, FAIL_FATALLY, VERIFY_ISOLATE_THREAD};

@NodeIntrinsic(value = ForeignCallNode.class)
public static native int runtimeCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, CEntryPointCreateIsolateParameters parameters);
Expand All @@ -158,6 +161,9 @@ public final class CEntryPointSnippets extends SubstrateTemplates implements Sni
@NodeIntrinsic(value = ForeignCallNode.class)
public static native int runtimeCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, Isolate isolate);

@NodeIntrinsic(value = ForeignCallNode.class)
public static native int runtimeCall(@ConstantNodeParameter ForeignCallDescriptor descriptor);

@NodeIntrinsic(value = ForeignCallNode.class)
public static native int runtimeCall(@ConstantNodeParameter ForeignCallDescriptor descriptor, IsolateThread thread);

Expand Down Expand Up @@ -484,7 +490,7 @@ private static int attachUnattachedThread(Isolate isolate, boolean startedByIsol
} else {
int error = VMThreads.singleton().attachThread(thread, startedByIsolate);
if (error != CEntryPointErrors.NO_ERROR) {
VMThreads.singleton().freeIsolateThread(thread);
VMThreads.singleton().freeCurrentIsolateThread();
return error;
}
}
Expand Down Expand Up @@ -536,16 +542,15 @@ private static int ensureJavaThread() {
}

@Snippet(allowMissingProbabilities = true)
public static int detachThreadSnippet() {
IsolateThread thread = CurrentIsolate.getCurrentThread();
return runtimeCall(DETACH_THREAD, thread);
public static int detachCurrentThreadSnippet() {
return runtimeCall(DETACH_CURRENT_THREAD);
}

@SubstrateForeignCallTarget(stubCallingConvention = false)
@Uninterruptible(reason = "Thread state going away.")
private static int detachThread(IsolateThread currentThread) {
private static int detachCurrentThread() {
try {
VMThreads.singleton().detachThread(currentThread);
VMThreads.singleton().detachCurrentThread();
} catch (Throwable t) {
return CEntryPointErrors.UNCAUGHT_EXCEPTION;
}
Expand All @@ -558,29 +563,64 @@ public static int tearDownIsolateSnippet() {
}

@SubstrateForeignCallTarget(stubCallingConvention = false)
@Uninterruptible(reason = "All code executed after VMThreads#tearDown must be uninterruptible")
@Uninterruptible(reason = "Tear-down in progress - may still execute interruptible Java code in the beginning.")
private static int tearDownIsolate() {
try {
/* Execute interruptible code. */
if (!initiateTearDownIsolateInterruptibly()) {
return CEntryPointErrors.UNSPECIFIED;
}

VMThreads.singleton().tearDown();
IsolateThread finalThread = CurrentIsolate.getCurrentThread();
int result = Isolates.tearDownCurrent();
// release the heap memory associated with final isolate thread
VMThreads.singleton().freeIsolateThread(finalThread);
WriteCurrentVMThreadNode.writeCurrentVMThread(WordFactory.nullPointer());
return result;
/* After threadExit(), only uninterruptible code may be executed. */
ThreadingSupportImpl.pauseRecurringCallback("Execution of arbitrary code is prohibited during the last teardown steps.");

/* Shut down VM thread. */
if (VMOperationControl.useDedicatedVMOperationThread()) {
VMOperationControl.shutdownAndDetachVMOperationThread();
}

/* Wait until all other threads exited on the OS-level. */
VMThreads.singleton().waitUntilDetachedThreadsExitedOnOSLevel();

IsolateThread currentThread = CurrentIsolate.getCurrentThread();
VMError.guarantee(VMThreads.firstThreadUnsafe().equal(currentThread));
VMError.guarantee(VMThreads.nextThread(VMThreads.firstThreadUnsafe()).isNull());

/* Now that we are truly single-threaded, notify listeners about isolate teardown. */
IsolateListenerSupport.singleton().onIsolateTeardown();

/* Free the native resources of the last thread. */
PlatformThreads.detach(currentThread);
PlatformThreads.singleton().closeOSThreadHandle(VMThreads.getOSThreadHandle(currentThread));

/* Free VM-internal native memory and tear down the Java heap. */
CodeInfoTable.tearDown();
NonmovableArrays.tearDown();
Heap.getHeap().tearDown();

/* Tear down the heap address space, including the image heap. */
int code = CommittedMemoryProvider.get().tearDown();
if (code != CEntryPointErrors.NO_ERROR) {
return code;
}

/* Free the last thread. */
VMThreads.singleton().freeCurrentIsolateThread();
return CEntryPointErrors.NO_ERROR;
} catch (Throwable t) {
return reportException(t);
}
}

@Uninterruptible(reason = "Used as a transition between uninterruptible and interruptible code", calleeMustBe = false)
@Uninterruptible(reason = "Tear-down in progress - still safe to execute interruptible Java code.", calleeMustBe = false)
private static boolean initiateTearDownIsolateInterruptibly() {
RuntimeSupport.executeTearDownHooks();
return PlatformThreads.singleton().tearDown();
if (!PlatformThreads.tearDownOtherThreads()) {
return false;
}

VMThreads.singleton().threadExit();
return true;
}

@Snippet(allowMissingProbabilities = true)
Expand Down Expand Up @@ -739,7 +779,7 @@ public static void registerLowerings(OptionValues options, Providers providers,
private final SnippetInfo enterByIsolate;

private final SnippetInfo returnFromJavaToC;
private final SnippetInfo detachThread;
private final SnippetInfo detachCurrentThread;
private final SnippetInfo reportException;
private final SnippetInfo tearDownIsolate;

Expand All @@ -755,7 +795,7 @@ private CEntryPointSnippets(OptionValues options, Providers providers, Map<Class
this.enterByIsolate = snippet(providers, CEntryPointSnippets.class, "enterByIsolateSnippet");

this.returnFromJavaToC = snippet(providers, CEntryPointSnippets.class, "returnFromJavaToCSnippet");
this.detachThread = snippet(providers, CEntryPointSnippets.class, "detachThreadSnippet");
this.detachCurrentThread = snippet(providers, CEntryPointSnippets.class, "detachCurrentThreadSnippet");
this.reportException = snippet(providers, CEntryPointSnippets.class, "reportExceptionSnippet");
this.tearDownIsolate = snippet(providers, CEntryPointSnippets.class, "tearDownIsolateSnippet");

Expand Down Expand Up @@ -815,7 +855,7 @@ public void lower(CEntryPointLeaveNode node, LoweringTool tool) {
args = new Arguments(returnFromJavaToC, node.graph().getGuardsStage(), tool.getLoweringStage());
break;
case DetachThread:
args = new Arguments(detachThread, node.graph().getGuardsStage(), tool.getLoweringStage());
args = new Arguments(detachCurrentThread, node.graph().getGuardsStage(), tool.getLoweringStage());
break;
case TearDownIsolate:
args = new Arguments(tearDownIsolate, node.graph().getGuardsStage(), tool.getLoweringStage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -473,28 +473,8 @@ public void assignMainThread() {
*/
}

/**
* Tear down all application threads (except the current one). This is called from an
* {@link CEntryPoint} exit action.
*
* @return true if the application threads have been torn down, false otherwise.
*/
public boolean tearDown() {
/* Tell all the threads that the VM is being torn down. */
boolean result = tearDownPlatformThreads();

// Detach last thread data
Thread thread = currentThread.get(CurrentIsolate.getCurrentThread());
if (thread != null) {
toTarget(thread).threadData.detach();
}
return result;
}

@Uninterruptible(reason = "Thread is detaching and holds the THREAD_MUTEX.")
public static void detachThread(IsolateThread vmThread) {
assert VMThreads.THREAD_MUTEX.isOwner(true);

public static void detach(IsolateThread vmThread) {
Thread thread = currentThread.get(vmThread);
if (thread != null) {
toTarget(thread).threadData.detach();
Expand Down Expand Up @@ -562,7 +542,7 @@ public void closeOSThreadHandle(OSThreadHandle threadHandle) {
}

/** Have each thread, except this one, tear itself down. */
private static boolean tearDownPlatformThreads() {
public static boolean tearDownOtherThreads() {
final Log trace = Log.noopLog().string("[PlatformThreads.tearDownPlatformThreads:").newline().flush();

/*
Expand All @@ -582,7 +562,8 @@ private static boolean tearDownPlatformThreads() {
Set<ExecutorService> pools = Collections.newSetFromMap(new IdentityHashMap<>());
Set<ExecutorService> poolsWithNonDaemons = Collections.newSetFromMap(new IdentityHashMap<>());
for (Thread thread : threads) {
if (thread == null || thread == currentThread.get()) {
assert thread != null;
if (thread == currentThread.get()) {
continue;
}

Expand Down
Loading
Loading