summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Misc/NEWS.d/next/Core_and_Builtins/2024-08-05-19-28-12.gh-issue-122697.17MvYl.rst2
-rw-r--r--Objects/obmalloc.c11
-rw-r--r--Python/gc_free_threading.c12
-rw-r--r--Python/pylifecycle.c4
-rw-r--r--Python/pystate.c2
5 files changed, 27 insertions, 4 deletions
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-08-05-19-28-12.gh-issue-122697.17MvYl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-05-19-28-12.gh-issue-122697.17MvYl.rst
new file mode 100644
index 0000000..34ee6a9
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-08-05-19-28-12.gh-issue-122697.17MvYl.rst
@@ -0,0 +1,2 @@
+Fixed memory leaks at interpreter shutdown in the free-threaded build, and
+also reporting of leaked memory blocks via :option:`-X showrefcount <-X>`.
diff --git a/Objects/obmalloc.c b/Objects/obmalloc.c
index a6a7180..dfeccfa 100644
--- a/Objects/obmalloc.c
+++ b/Objects/obmalloc.c
@@ -1109,9 +1109,12 @@ free_delayed(uintptr_t ptr)
#ifndef Py_GIL_DISABLED
free_work_item(ptr);
#else
- if (_PyRuntime.stoptheworld.world_stopped) {
- // Free immediately if the world is stopped, including during
- // interpreter shutdown.
+ PyInterpreterState *interp = _PyInterpreterState_GET();
+ if (_PyInterpreterState_GetFinalizing(interp) != NULL ||
+ interp->stoptheworld.world_stopped)
+ {
+ // Free immediately during interpreter shutdown or if the world is
+ // stopped.
free_work_item(ptr);
return;
}
@@ -1474,6 +1477,8 @@ _PyInterpreterState_FinalizeAllocatedBlocks(PyInterpreterState *interp)
{
#ifdef WITH_MIMALLOC
if (_PyMem_MimallocEnabled()) {
+ Py_ssize_t leaked = _PyInterpreterState_GetAllocatedBlocks(interp);
+ interp->runtime->obmalloc.interpreter_leaks += leaked;
return;
}
#endif
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 1e02db0..543bee2 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -55,6 +55,7 @@ struct collection_state {
struct visitor_args base;
PyInterpreterState *interp;
GCState *gcstate;
+ _PyGC_Reason reason;
Py_ssize_t collected;
Py_ssize_t uncollectable;
Py_ssize_t long_lived_total;
@@ -572,6 +573,16 @@ scan_heap_visitor(const mi_heap_t *heap, const mi_heap_area_t *area,
worklist_push(&state->unreachable, op);
}
}
+ else if (state->reason == _Py_GC_REASON_SHUTDOWN &&
+ _PyObject_HasDeferredRefcount(op))
+ {
+ // Disable deferred refcounting for reachable objects as well during
+ // interpreter shutdown. This ensures that these objects are collected
+ // immediately when their last reference is removed.
+ disable_deferred_refcounting(op);
+ merge_refcount(op, 0);
+ state->long_lived_total++;
+ }
else {
// object is reachable, restore `ob_tid`; we're done with these objects
gc_restore_tid(op);
@@ -1228,6 +1239,7 @@ gc_collect_main(PyThreadState *tstate, int generation, _PyGC_Reason reason)
struct collection_state state = {
.interp = interp,
.gcstate = gcstate,
+ .reason = reason,
};
gc_collect_internal(interp, &state, generation);
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 6b641c0..3a41c64 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -28,6 +28,7 @@
#include "pycore_sliceobject.h" // _PySlice_Fini()
#include "pycore_sysmodule.h" // _PySys_ClearAuditHooks()
#include "pycore_traceback.h" // _Py_DumpTracebackThreads()
+#include "pycore_typeid.h" // _PyType_FinalizeIdPool()
#include "pycore_typeobject.h" // _PyTypes_InitTypes()
#include "pycore_typevarobject.h" // _Py_clear_generic_types()
#include "pycore_unicodeobject.h" // _PyUnicode_InitTypes()
@@ -1832,6 +1833,9 @@ finalize_interp_types(PyInterpreterState *interp)
_PyTypes_FiniTypes(interp);
_PyTypes_Fini(interp);
+#ifdef Py_GIL_DISABLED
+ _PyType_FinalizeIdPool(interp);
+#endif
_PyCode_Fini(interp);
diff --git a/Python/pystate.c b/Python/pystate.c
index 8f4818c..bba88b7 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -20,7 +20,7 @@
#include "pycore_runtime_init.h" // _PyRuntimeState_INIT
#include "pycore_sysmodule.h" // _PySys_Audit()
#include "pycore_obmalloc.h" // _PyMem_obmalloc_state_on_heap()
-#include "pycore_typeid.h" // _PyType_FinalizeIdPool
+#include "pycore_typeid.h" // _PyType_FinalizeThreadLocalRefcounts()
/* --------------------------------------------------------------------------
CAUTION