@@ -212,6 +212,7 @@ public void setNativeWrapper(PThreadState nativeWrapper) {
212
212
}
213
213
214
214
public void dispose () {
215
+ // This method may be called twice on the same object.
215
216
ReleaseHandleNode releaseHandleNode = ReleaseHandleNodeGen .getUncached ();
216
217
if (dict != null && dict .getNativeWrapper () != null ) {
217
218
releaseHandleNode .execute (dict .getNativeWrapper ());
@@ -854,6 +855,8 @@ public void finalizeContext() {
854
855
try (GilNode .UncachedAcquire gil = GilNode .uncachedAcquire ()) {
855
856
shutdownThreads ();
856
857
runShutdownHooks ();
858
+ disposeThreadStates ();
859
+ cleanupCApiResources ();
857
860
}
858
861
}
859
862
@@ -891,10 +894,26 @@ public void runShutdownHooks() {
891
894
for (ShutdownHook h : shutdownHooks ) {
892
895
h .call (this );
893
896
}
894
- assert threadStateMapping != null ;
895
- for (PythonThreadState threadState : threadStateMapping .values ()) {
896
- threadState .dispose ();
897
+ }
898
+
899
+ /**
900
+ * Release all resources held by the thread states. This function needs to run as long as the
901
+ * context is still valid because it may call into LLVM to release handles.
902
+ */
903
+ @ TruffleBoundary
904
+ private void disposeThreadStates () {
905
+ for (PythonThreadState ts : threadStateMapping .values ()) {
906
+ ts .dispose ();
897
907
}
908
+ threadStateMapping .clear ();
909
+ }
910
+
911
+ /**
912
+ * Release all native wrappers of singletons. This function needs to run as long as the context
913
+ * is still valid because it may call into LLVM to release handles.
914
+ */
915
+ @ TruffleBoundary
916
+ private void cleanupCApiResources () {
898
917
ReleaseHandleNode releaseHandleNode = ReleaseHandleNodeGen .getUncached ();
899
918
for (PythonNativeWrapper singletonNativeWrapper : singletonNativePtrs ) {
900
919
if (singletonNativeWrapper != null ) {
0 commit comments