diff --git a/Include/internal/pycore_critical_section.h b/Include/internal/pycore_critical_section.h index 60b6fc4a72e88f..2a2846b1296b90 100644 --- a/Include/internal/pycore_critical_section.h +++ b/Include/internal/pycore_critical_section.h @@ -50,6 +50,15 @@ extern "C" { // Asserts that the mutex for the given object is locked. The mutex must // be held by the top-most critical section otherwise there's the // possibility that the mutex would be swalled out in some code paths. +// +// NOTE: We use Py_REFCNT(op) != 1 instead of +// !PyUnstable_Object_IsUniquelyReferenced(op) because the free threading +// GC can change an object's ob_tid (it overwrites ob_tid and later +// restores it from the mimalloc segment). This means +// PyUnstable_Object_IsUniquelyReferenced() may spuriously return false +// after a GC collection, even though the thread may still have exclusive +// access to the object. The refcount check is a looser but still catches +// most misuses. #ifdef Py_DEBUG # define _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op) \ diff --git a/InternalDocs/garbage_collector.md b/InternalDocs/garbage_collector.md index a7d872f3ec4392..94e6fb05b68d6f 100644 --- a/InternalDocs/garbage_collector.md +++ b/InternalDocs/garbage_collector.md @@ -153,7 +153,11 @@ pointer-sized (that is, eight bytes on a 64-bit platform). The garbage collector also temporarily repurposes the `ob_tid` (thread ID) and `ob_ref_local` (local reference count) fields for other purposes during -collections. +collections. The `ob_tid` field is later restored from the containing +mimalloc segment data structure. In some cases, such as when the original +allocating thread exits, this can result in a different `ob_tid` value. +Code should not rely on `ob_tid` being stable across operations that may +trigger garbage collection. C APIs diff --git a/Objects/funcobject.c b/Objects/funcobject.c index ee0c46a95b9708..8099b82f4835fb 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1126,10 +1126,6 @@ func_dealloc(PyObject *self) if (_PyObject_ResurrectEnd(self)) { return; } -#if _Py_TIER2 - _Py_Executors_InvalidateDependency(_PyInterpreterState_GET(), self, 1); - _PyJit_Tracer_InvalidateDependency(_PyThreadState_GET(), self); -#endif _PyObject_GC_UNTRACK(op); FT_CLEAR_WEAKREFS(self, op->func_weakreflist); (void)func_clear((PyObject*)op);