diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java index dc2a30be35..40d1d821e7 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/builtins/modules/FaulthandlerModuleBuiltins.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * The Universal Permissive License (UPL), Version 1.0 @@ -121,7 +121,7 @@ public void postInitialize(Python3Core core) { } @TruffleBoundary - private static void dumpTraceback(PythonLanguage language, PrintWriter writer) { + private static synchronized void dumpTraceback(PythonLanguage language, PrintWriter writer) { writer.println(); writer.println(Thread.currentThread()); if (PythonOptions.isPExceptionWithJavaStacktrace(language)) { @@ -262,7 +262,9 @@ private static void doDumpLater(Node inliningTarget, PythonModule module, long t do { sleepInterruptibly(inliningTarget, timeoutNs); long timeoutS = timeoutNs / 1_000_000_000; - newRawFdPrintWriter(fd).printf("Timeout (%d:%02d:%02d)!%n", timeoutS / 3600, timeoutS / 60, timeoutS); + PrintWriter timeoutWriter = newRawFdPrintWriter(fd); + timeoutWriter.printf("Timeout (%d:%02d:%02d)!%n", timeoutS / 3600, timeoutS / 60, timeoutS); + timeoutWriter.flush(); try { DumpTracebackNode.dump(context.getLanguage(), context, fd, fileObj, true); if (exit) { diff --git a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java index 1b91e7d4d7..5e64f0a3ec 100644 --- a/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java +++ b/graalpython/com.oracle.graal.python/src/com/oracle/graal/python/runtime/PythonContext.java @@ -470,7 +470,7 @@ public void setContextVarsContext(PContextVarsContext contextVarsContext) { this.contextVarsContext = contextVarsContext; } - public void dispose(PythonContext context, boolean canRunGuestCode) { + public void dispose(PythonContext context, boolean canRunGuestCode, boolean clearNativeThreadLocalVarPointer) { // This method may be called twice on the same object. /* @@ -498,10 +498,10 @@ public void dispose(PythonContext context, boolean canRunGuestCode) { * precaution, we just skip this if we cannot run guest code, because it may invoke * LLVM. */ - if (nativeThreadLocalVarPointer != null && canRunGuestCode) { + if (nativeThreadLocalVarPointer != null && canRunGuestCode && clearNativeThreadLocalVarPointer) { CStructAccess.WritePointerNode.writeUncached(nativeThreadLocalVarPointer, 0, context.getNativeNull()); - nativeThreadLocalVarPointer = null; } + nativeThreadLocalVarPointer = null; } public Object getTraceFun() { @@ -2097,8 +2097,9 @@ public void runShutdownHooks() { */ @TruffleBoundary private void disposeThreadStates() { - for (PythonThreadState ts : threadStateMapping.values()) { - ts.dispose(this, true); + Thread currentThread = Thread.currentThread(); + for (Map.Entry entry : threadStateMapping.entrySet()) { + entry.getValue().dispose(this, true, entry.getKey() == currentThread); } threadStateMapping.clear(); } @@ -2581,7 +2582,7 @@ public synchronized void disposeThread(Thread thread, boolean canRunGuestCode) { } ts.shutdown(); threadStateMapping.remove(thread); - ts.dispose(this, canRunGuestCode); + ts.dispose(this, canRunGuestCode, thread == Thread.currentThread()); releaseSentinelLock(ts.sentinelLock); getSharedMultiprocessingData().removeChildContextThread(PThread.getThreadId(thread)); } diff --git a/graalpython/lib-python/3/test/conftest.toml b/graalpython/lib-python/3/test/conftest.toml index 6e20188cd8..e6618240f9 100644 --- a/graalpython/lib-python/3/test/conftest.toml +++ b/graalpython/lib-python/3/test/conftest.toml @@ -33,6 +33,7 @@ partial_splits_individual_tests = true selector = [ 'test_multiprocessing_spawn', 'test_multiprocessing_main_handling', + 'test_tarfile', ] diff --git a/graalpython/lib-python/3/test/test_weakref.py b/graalpython/lib-python/3/test/test_weakref.py index 8d43595394..17ded41dad 100644 --- a/graalpython/lib-python/3/test/test_weakref.py +++ b/graalpython/lib-python/3/test/test_weakref.py @@ -786,6 +786,7 @@ def cb(self, ignore): del alist[:] gc.collect() + gc_collect() # <- GraalPy change self.assertEqual(alist, []) def test_gc_during_ref_creation(self): @@ -798,6 +799,7 @@ def check_gc_during_creation(self, makeref): thresholds = gc.get_threshold() gc.set_threshold(1, 1, 1) gc.collect() + gc_collect() # <- GraalPy change class A: pass @@ -902,6 +904,7 @@ def test_ordering(self): # Same when dead. del x, y gc.collect() + gc_collect() # <- GraalPy change for op in ops: self.assertRaises(TypeError, op, a, b) @@ -914,6 +917,7 @@ def test_hashing(self): self.assertEqual(hash(a), hash(42)) del x, y gc.collect() + gc_collect() # <- GraalPy change # Dead weakrefs: # - retain their hash is they were hashed when alive; # - otherwise, cannot be hashed. @@ -1192,6 +1196,7 @@ def _ne(a, b): _eq(a, ALWAYS_EQ) del x, y, z gc.collect() + gc_collect() # <- GraalPy change # Dead WeakMethods compare by identity refs = a, b, c, d, e, f for q in refs: @@ -1236,9 +1241,11 @@ def check_len_cycles(self, dict_type, cons): pass del items gc.collect() + gc_collect() # <- GraalPy change n1 = len(dct) del it gc.collect() + gc_collect() # <- GraalPy change n2 = len(dct) # one item may be kept alive inside the iterator self.assertIn(n1, (0, 1)) @@ -1257,6 +1264,7 @@ def check_len_race(self, dict_type, cons): for th in range(1, 100): N = 20 gc.collect(0) + gc_collect() # <- GraalPy change gc.set_threshold(th, th, th) items = [RefCycle() for i in range(N)] dct = dict_type(cons(o) for o in items) @@ -1428,6 +1436,7 @@ def check_weak_destroy_while_iterating(self, dict, objects, iter_name): # Destroy an object del objects[-1] gc.collect() # just in case + gc_collect() # <- GraalPy change # We have removed either the first consumed object, or another one self.assertIn(len(list(it)), [len(objects), len(objects) - 1]) del it @@ -1508,10 +1517,12 @@ def testcontext(): # Schedule a key/value for removal and recreate it v = objects.pop().arg gc.collect() # just in case + gc_collect() # <- GraalPy change yield Object(v), v finally: it = None # should commit all removals gc.collect() + gc_collect() # <- GraalPy change self.check_weak_destroy_and_mutate_while_iterating(dict, testcontext) # Issue #21173: len() fragile when keys are both implicitly and # explicitly removed. @@ -1535,10 +1546,12 @@ def testcontext(): # Schedule a key/value for removal and recreate it k = objects.pop().arg gc.collect() # just in case + gc_collect() # <- GraalPy change yield k, Object(k) finally: it = None # should commit all removals gc.collect() + gc_collect() # <- GraalPy change self.check_weak_destroy_and_mutate_while_iterating(dict, testcontext) dict, objects = self.make_weak_valued_dict() self.check_weak_del_and_len_while_iterating(dict, testcontext) @@ -1907,6 +1920,7 @@ def pop_and_collect(lst): lst.pop(i) if gc_ctr % 10000 == 0: gc.collect() # just in case + gc_collect() # <- GraalPy change self.assertIn(type_, (weakref.WeakKeyDictionary, weakref.WeakValueDictionary))