From 7210685afc3fbdd5fc22b972e80f0abafc982abf Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Tue, 17 Jun 2025 21:26:50 +0800 Subject: [PATCH 01/15] remove null checking for weakref --- Objects/moduleobject.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index ba86b41e945e9d..be702d3f45287c 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -826,8 +826,7 @@ module_dealloc(PyObject *self) if (verbose && m->md_name) { PySys_FormatStderr("# destroy %U\n", m->md_name); } - if (m->md_weaklist != NULL) - PyObject_ClearWeakRefs((PyObject *) m); + PyObject_ClearWeakRefs(self); /* bpo-39824: Don't call m_free() if m_size > 0 and md_state=NULL */ if (m->md_def && m->md_def->m_free From 4289ddc8ae6383849b93dcbcd0c3a9f112b52c43 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Tue, 17 Jun 2025 22:35:11 +0800 Subject: [PATCH 02/15] add blurb news entry --- .../2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst new file mode 100644 index 00000000000000..d5188652714154 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst @@ -0,0 +1,2 @@ +Do not check ``md_weaklist`` in :func:`module_dealloc` and always call +:func:`PyObject_ClearWeakRefs` unconditionally. From 864f483eabf65aad7a2f1bcd29eb2cf135b89ab9 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Tue, 17 Jun 2025 23:05:12 +0800 Subject: [PATCH 03/15] add module dealloc test --- Lib/test/test_weakref.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 4c7c900eb56ae1..ae575be3831815 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -9,6 +9,7 @@ import copy import threading import time +import types import random import textwrap @@ -2252,6 +2253,13 @@ def test_names(self): self.assertEqual(obj.__name__, name) self.assertEqual(obj.__qualname__, name) + def test_module_dealloc(self): + mod = types.ModuleType("temp_mod") + r = weakref.ref(mod) + self.assertIsNotNone(r, "weak ref to a module should not be None") + + del mod + libreftest = """ Doctest for examples in the library reference: weakref.rst From e8fb5cf2a9a939a5332d2d2d3b2539353d0bfc88 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Wed, 18 Jun 2025 00:01:39 +0800 Subject: [PATCH 04/15] update news --- .../2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst index d5188652714154..5513bd644770fe 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst @@ -1,2 +1 @@ -Do not check ``md_weaklist`` in :func:`module_dealloc` and always call -:func:`PyObject_ClearWeakRefs` unconditionally. +Fix potential weakref races on module ojbect under free-threaded build. From 32e7860acb34282c1fd004953ce5e6260a7609e2 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Wed, 18 Jun 2025 14:46:04 +0800 Subject: [PATCH 05/15] update news entry --- .../2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst index 5513bd644770fe..0acdf69ce95217 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst @@ -1 +1,2 @@ -Fix potential weakref races on module ojbect under free-threaded build. +Fix potential :mod:`weakref` races on module objects on the :term:`free threaded ` build. From 5f0996e77c2b8e75552ba3199f48f5fae1e77437 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Wed, 18 Jun 2025 15:33:26 +0800 Subject: [PATCH 06/15] stress module weakref test with multiple threads --- Lib/test/test_weakref.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index ae575be3831815..b38c3836c263af 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -2253,11 +2253,31 @@ def test_names(self): self.assertEqual(obj.__name__, name) self.assertEqual(obj.__qualname__, name) - def test_module_dealloc(self): + def test_module_weakref(self): mod = types.ModuleType("temp_mod") + common_ref = weakref.ref(mod) + threads = [] + n_threads = 5 + b = threading.Barrier(n_threads) + + def weakref_mod_worker(): + b.wait() + r = weakref.ref(mod) + rr = r() + self.assertIs(rr, mod) + self.assertIs(rr, common_ref()) + + for i in range(n_threads): + t = threading.Thread(target=weakref_mod_worker) + threads.append(t) + t.start() + + for t in threads: + t.join() + r = weakref.ref(mod) self.assertIsNotNone(r, "weak ref to a module should not be None") - + self.assertIs(r(), common_ref()) del mod From a72123912744f359b3efbc8c950b5df18689b8c2 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Wed, 18 Jun 2025 16:44:55 +0800 Subject: [PATCH 07/15] add `requires_working_threading` --- Lib/test/test_weakref.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index b38c3836c263af..10ead67fa342f4 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -2253,11 +2253,12 @@ def test_names(self): self.assertEqual(obj.__name__, name) self.assertEqual(obj.__qualname__, name) + @threading_helper.requires_working_threading() def test_module_weakref(self): mod = types.ModuleType("temp_mod") common_ref = weakref.ref(mod) threads = [] - n_threads = 5 + n_threads = 10 b = threading.Barrier(n_threads) def weakref_mod_worker(): From e1a0f5944f4e60edb5c32a6c247b15f5dc984749 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Wed, 18 Jun 2025 21:52:04 +0800 Subject: [PATCH 08/15] add comments on newly-added test --- Lib/test/test_weakref.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 10ead67fa342f4..35030734a08bbf 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -2254,7 +2254,9 @@ def test_names(self): self.assertEqual(obj.__qualname__, name) @threading_helper.requires_working_threading() + @unittest.skipUnless(support.Py_GIL_DISABLED, 'only used under free-threaded build') def test_module_weakref(self): + # gh-135607: Avoid potential races on module weaklist under free-threaded build mod = types.ModuleType("temp_mod") common_ref = weakref.ref(mod) threads = [] From 5a45315d6d8eb37e74e95fe539e629d7c8540131 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Thu, 19 Jun 2025 10:23:11 +0800 Subject: [PATCH 09/15] clear weakrefs unconditionally in extension modules --- Modules/_collectionsmodule.c | 4 +--- Modules/_elementtree.c | 3 +-- Modules/_functoolsmodule.c | 8 ++------ Modules/_io/bufferedio.c | 3 +-- Modules/_io/fileio.c | 4 +--- Modules/_io/iobase.c | 3 +-- Modules/_io/textio.c | 3 +-- Modules/_io/winconsoleio.c | 3 +-- Modules/_queuemodule.c | 4 +--- Modules/_sqlite/blob.c | 4 +--- Modules/_sqlite/cursor.c | 4 +--- Modules/_sre/sre.c | 5 +---- Modules/_struct.c | 4 +--- Modules/_testcapi/heaptype.c | 3 +-- Modules/_testlimitedcapi/heaptype_relative.c | 4 +--- Modules/_threadmodule.c | 8 ++------ Modules/_zoneinfo.c | 4 +--- Modules/arraymodule.c | 4 +--- Modules/itertoolsmodule.c | 3 +-- Modules/mmapmodule.c | 3 +-- 20 files changed, 22 insertions(+), 59 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index ad670293ec5b6a..d716b86fd308e5 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1532,9 +1532,7 @@ deque_dealloc(PyObject *self) Py_ssize_t i; PyObject_GC_UnTrack(deque); - if (deque->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); if (deque->leftblock != NULL) { (void)deque_clear(self); assert(deque->leftblock != NULL); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index fe24629f9f6f70..3aa6ba329e6968 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -690,8 +690,7 @@ element_dealloc(PyObject *op) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); /* element_gc_clear clears all references and deallocates extra */ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 354dbad84b5099..5fdad08a93eb6c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -351,9 +351,7 @@ partial_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - if (partialobject_CAST(self)->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); (void)partial_clear(self); tp->tp_free(self); Py_DECREF(tp); @@ -1621,9 +1619,7 @@ lru_cache_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(obj); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(obj); - if (obj->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)lru_cache_tp_clear(op); tp->tp_free(obj); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4724e97982f349..43190dab2496ba 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -421,8 +421,7 @@ buffered_dealloc(PyObject *op) return; _PyObject_GC_UNTRACK(self); self->ok = 0; - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 8fcb27049d6c7c..ef49d9ab29dedf 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -570,9 +570,7 @@ fileio_dealloc(PyObject *op) PyMem_Free(self->stat_atopen); self->stat_atopen = NULL; } - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)fileio_clear(op); PyTypeObject *tp = Py_TYPE(op); diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index cd4c7e7cead277..4ba8bc9552d0f2 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -383,8 +383,7 @@ iobase_dealloc(PyObject *op) } PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3808ecdceb9b70..3613fc61f70ffe 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1469,8 +1469,7 @@ textiowrapper_dealloc(PyObject *op) return; self->ok = 0; _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); (void)textiowrapper_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 3e783b9da45652..f4c22d0a8c6e75 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -518,8 +518,7 @@ winconsoleio_dealloc(PyObject *op) if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 3ee14b61b821d6..11cf6eae0ac2ff 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -221,9 +221,7 @@ simplequeue_dealloc(PyObject *op) PyObject_GC_UnTrack(self); (void)simplequeue_clear(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index 35d090e3ca2dce..adfa29e191eb41 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -56,9 +56,7 @@ blob_dealloc(PyObject *op) close_blob(self); - if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 7943bfcca3679d..73fb26eb3e73d7 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -185,9 +185,7 @@ cursor_dealloc(PyObject *op) pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 602d0ab8588f62..103f68ec80874a 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -736,10 +736,7 @@ pattern_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PatternObject *obj = _PatternObject_CAST(self); - if (obj->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); (void)pattern_clear(self); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_struct.c b/Modules/_struct.c index c36079f1eb8886..149c012fd5c2f6 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1794,9 +1794,7 @@ s_dealloc(PyObject *op) PyStructObject *s = PyStructObject_CAST(op); PyTypeObject *tp = Py_TYPE(s); PyObject_GC_UnTrack(s); - if (s->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); if (s->s_codes != NULL) { PyMem_Free(s->s_codes); } diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 257e0256655976..26d6c3eb5189db 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -1034,8 +1034,7 @@ heapctypewithweakref_dealloc(PyObject *op) { HeapCTypeWithWeakrefObject *self = (HeapCTypeWithWeakrefObject*)op; PyTypeObject *tp = Py_TYPE(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs((PyObject *) self); Py_XDECREF(self->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c index fc278a70b77d31..9bfb5eeee8e546 100644 --- a/Modules/_testlimitedcapi/heaptype_relative.c +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -297,9 +297,7 @@ heapctypewithrelativeweakref_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp); - if (data->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); Py_XDECREF(data->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 150a266b521736..dd59e2cd6beb19 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1365,9 +1365,7 @@ static void localdummy_dealloc(PyObject *op) { localdummyobject *self = localdummyobject_CAST(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); @@ -1537,9 +1535,7 @@ local_dealloc(PyObject *op) localobject *self = localobject_CAST(op); /* Weakrefs must be invalidated right now, otherwise they can be used from code called below, which is very dangerous since Py_REFCNT(self) == 0 */ - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); PyObject_GC_UnTrack(self); (void)local_clear(op); PyTypeObject *tp = Py_TYPE(self); diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index abd53436b21b29..683f41c7677081 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -375,9 +375,7 @@ zoneinfo_dealloc(PyObject *obj_self) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(obj_self); - } + PyObject_ClearWeakRefs(obj_self); if (self->trans_list_utc != NULL) { PyMem_Free(self->trans_list_utc); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 401a3a7072b846..d49cf208c29541 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -728,9 +728,7 @@ array_dealloc(PyObject *op) PyObject_GC_UnTrack(op); arrayobject *self = arrayobject_CAST(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); if (self->ob_item != NULL) { PyMem_Free(self->ob_item); } diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 2003546ce84cef..c4f4b769505821 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1018,8 +1018,7 @@ static int tee_clear(PyObject *op) { teeobject *to = teeobject_CAST(op); - if (to->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); Py_CLEAR(to->dataobj); return 0; } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 7c4eb05488eb33..b2a00404812cd9 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -163,8 +163,7 @@ mmap_object_dealloc(PyObject *op) Py_END_ALLOW_THREADS #endif /* UNIX */ - if (m_obj->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); tp->tp_free(m_obj); Py_DECREF(tp); From e74fbd06a82c1e6ab6cc33bb88f288e41d942763 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Thu, 19 Jun 2025 10:30:29 +0800 Subject: [PATCH 10/15] remove introduced test --- Lib/test/test_weakref.py | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/Lib/test/test_weakref.py b/Lib/test/test_weakref.py index 35030734a08bbf..4c7c900eb56ae1 100644 --- a/Lib/test/test_weakref.py +++ b/Lib/test/test_weakref.py @@ -9,7 +9,6 @@ import copy import threading import time -import types import random import textwrap @@ -2253,36 +2252,6 @@ def test_names(self): self.assertEqual(obj.__name__, name) self.assertEqual(obj.__qualname__, name) - @threading_helper.requires_working_threading() - @unittest.skipUnless(support.Py_GIL_DISABLED, 'only used under free-threaded build') - def test_module_weakref(self): - # gh-135607: Avoid potential races on module weaklist under free-threaded build - mod = types.ModuleType("temp_mod") - common_ref = weakref.ref(mod) - threads = [] - n_threads = 10 - b = threading.Barrier(n_threads) - - def weakref_mod_worker(): - b.wait() - r = weakref.ref(mod) - rr = r() - self.assertIs(rr, mod) - self.assertIs(rr, common_ref()) - - for i in range(n_threads): - t = threading.Thread(target=weakref_mod_worker) - threads.append(t) - t.start() - - for t in threads: - t.join() - - r = weakref.ref(mod) - self.assertIsNotNone(r, "weak ref to a module should not be None") - self.assertIs(r(), common_ref()) - del mod - libreftest = """ Doctest for examples in the library reference: weakref.rst From 6e83b5a25fd33f25aab4872bd9cf1226e71e4774 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Thu, 19 Jun 2025 14:50:10 +0800 Subject: [PATCH 11/15] Revert "clear weakrefs unconditionally in extension modules" This reverts commit 9024079b099aa25c34304614a35536191ae79a35. --- Modules/_collectionsmodule.c | 4 +++- Modules/_elementtree.c | 3 ++- Modules/_functoolsmodule.c | 8 ++++++-- Modules/_io/bufferedio.c | 3 ++- Modules/_io/fileio.c | 4 +++- Modules/_io/iobase.c | 3 ++- Modules/_io/textio.c | 3 ++- Modules/_io/winconsoleio.c | 3 ++- Modules/_queuemodule.c | 4 +++- Modules/_sqlite/blob.c | 4 +++- Modules/_sqlite/cursor.c | 4 +++- Modules/_sre/sre.c | 5 ++++- Modules/_struct.c | 4 +++- Modules/_testcapi/heaptype.c | 3 ++- Modules/_testlimitedcapi/heaptype_relative.c | 4 +++- Modules/_threadmodule.c | 8 ++++++-- Modules/_zoneinfo.c | 4 +++- Modules/arraymodule.c | 4 +++- Modules/itertoolsmodule.c | 3 ++- Modules/mmapmodule.c | 3 ++- 20 files changed, 59 insertions(+), 22 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index d716b86fd308e5..ad670293ec5b6a 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1532,7 +1532,9 @@ deque_dealloc(PyObject *self) Py_ssize_t i; PyObject_GC_UnTrack(deque); - PyObject_ClearWeakRefs(self); + if (deque->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); + } if (deque->leftblock != NULL) { (void)deque_clear(self); assert(deque->leftblock != NULL); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 3aa6ba329e6968..fe24629f9f6f70 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -690,7 +690,8 @@ element_dealloc(PyObject *op) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs(op); /* element_gc_clear clears all references and deallocates extra */ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 5fdad08a93eb6c..354dbad84b5099 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -351,7 +351,9 @@ partial_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(self); + if (partialobject_CAST(self)->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); + } (void)partial_clear(self); tp->tp_free(self); Py_DECREF(tp); @@ -1619,7 +1621,9 @@ lru_cache_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(obj); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(obj); - PyObject_ClearWeakRefs(op); + if (obj->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } (void)lru_cache_tp_clear(op); tp->tp_free(obj); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 43190dab2496ba..4724e97982f349 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -421,7 +421,8 @@ buffered_dealloc(PyObject *op) return; _PyObject_GC_UNTRACK(self); self->ok = 0; - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs(op); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index ef49d9ab29dedf..8fcb27049d6c7c 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -570,7 +570,9 @@ fileio_dealloc(PyObject *op) PyMem_Free(self->stat_atopen); self->stat_atopen = NULL; } - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } (void)fileio_clear(op); PyTypeObject *tp = Py_TYPE(op); diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 4ba8bc9552d0f2..cd4c7e7cead277 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -383,7 +383,8 @@ iobase_dealloc(PyObject *op) } PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3613fc61f70ffe..3808ecdceb9b70 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1469,7 +1469,8 @@ textiowrapper_dealloc(PyObject *op) return; self->ok = 0; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs(op); (void)textiowrapper_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index f4c22d0a8c6e75..3e783b9da45652 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -518,7 +518,8 @@ winconsoleio_dealloc(PyObject *op) if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 11cf6eae0ac2ff..3ee14b61b821d6 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -221,7 +221,9 @@ simplequeue_dealloc(PyObject *op) PyObject_GC_UnTrack(self); (void)simplequeue_clear(op); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index adfa29e191eb41..35d090e3ca2dce 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -56,7 +56,9 @@ blob_dealloc(PyObject *op) close_blob(self); - PyObject_ClearWeakRefs(op); + if (self->in_weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 73fb26eb3e73d7..7943bfcca3679d 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -185,7 +185,9 @@ cursor_dealloc(PyObject *op) pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(op); + if (self->in_weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 103f68ec80874a..602d0ab8588f62 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -736,7 +736,10 @@ pattern_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(self); + PatternObject *obj = _PatternObject_CAST(self); + if (obj->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); + } (void)pattern_clear(self); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_struct.c b/Modules/_struct.c index 149c012fd5c2f6..c36079f1eb8886 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1794,7 +1794,9 @@ s_dealloc(PyObject *op) PyStructObject *s = PyStructObject_CAST(op); PyTypeObject *tp = Py_TYPE(s); PyObject_GC_UnTrack(s); - PyObject_ClearWeakRefs(op); + if (s->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } if (s->s_codes != NULL) { PyMem_Free(s->s_codes); } diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 26d6c3eb5189db..257e0256655976 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -1034,7 +1034,8 @@ heapctypewithweakref_dealloc(PyObject *op) { HeapCTypeWithWeakrefObject *self = (HeapCTypeWithWeakrefObject*)op; PyTypeObject *tp = Py_TYPE(self); - PyObject_ClearWeakRefs((PyObject *) self); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); Py_XDECREF(self->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c index 9bfb5eeee8e546..fc278a70b77d31 100644 --- a/Modules/_testlimitedcapi/heaptype_relative.c +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -297,7 +297,9 @@ heapctypewithrelativeweakref_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp); - PyObject_ClearWeakRefs(self); + if (data->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); + } Py_XDECREF(data->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index dd59e2cd6beb19..150a266b521736 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1365,7 +1365,9 @@ static void localdummy_dealloc(PyObject *op) { localdummyobject *self = localdummyobject_CAST(op); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); @@ -1535,7 +1537,9 @@ local_dealloc(PyObject *op) localobject *self = localobject_CAST(op); /* Weakrefs must be invalidated right now, otherwise they can be used from code called below, which is very dangerous since Py_REFCNT(self) == 0 */ - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } PyObject_GC_UnTrack(self); (void)local_clear(op); PyTypeObject *tp = Py_TYPE(self); diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 683f41c7677081..abd53436b21b29 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -375,7 +375,9 @@ zoneinfo_dealloc(PyObject *obj_self) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(obj_self); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(obj_self); + } if (self->trans_list_utc != NULL) { PyMem_Free(self->trans_list_utc); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index d49cf208c29541..401a3a7072b846 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -728,7 +728,9 @@ array_dealloc(PyObject *op) PyObject_GC_UnTrack(op); arrayobject *self = arrayobject_CAST(op); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) { + PyObject_ClearWeakRefs(op); + } if (self->ob_item != NULL) { PyMem_Free(self->ob_item); } diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index c4f4b769505821..2003546ce84cef 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -1018,7 +1018,8 @@ static int tee_clear(PyObject *op) { teeobject *to = teeobject_CAST(op); - PyObject_ClearWeakRefs(op); + if (to->weakreflist != NULL) + PyObject_ClearWeakRefs(op); Py_CLEAR(to->dataobj); return 0; } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index b2a00404812cd9..7c4eb05488eb33 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -163,7 +163,8 @@ mmap_object_dealloc(PyObject *op) Py_END_ALLOW_THREADS #endif /* UNIX */ - PyObject_ClearWeakRefs(op); + if (m_obj->weakreflist != NULL) + PyObject_ClearWeakRefs(op); tp->tp_free(m_obj); Py_DECREF(tp); From ae32f686b7f8bf3e5b50625cfd22b2dc385ed53e Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Thu, 19 Jun 2025 17:05:03 +0800 Subject: [PATCH 12/15] call `PyObject_ClearWeakRefs` unconditionally --- Modules/_collectionsmodule.c | 4 +--- Modules/_elementtree.c | 3 +-- Modules/_functoolsmodule.c | 8 ++------ Modules/_io/bufferedio.c | 6 ++---- Modules/_io/bytesio.c | 3 +-- Modules/_io/fileio.c | 4 +--- Modules/_io/iobase.c | 3 +-- Modules/_io/stringio.c | 4 +--- Modules/_io/textio.c | 3 +-- Modules/_io/winconsoleio.c | 3 +-- Modules/_queuemodule.c | 4 +--- Modules/_sqlite/blob.c | 4 +--- Modules/_sqlite/cursor.c | 4 +--- Modules/_sre/sre.c | 5 +---- Modules/_struct.c | 4 +--- Modules/_testcapi/heaptype.c | 3 +-- Modules/_testlimitedcapi/heaptype_relative.c | 4 +--- Modules/_zoneinfo.c | 4 +--- Modules/arraymodule.c | 4 +--- Modules/mmapmodule.c | 3 +-- Objects/classobject.c | 3 +-- Objects/codeobject.c | 4 +--- Objects/funcobject.c | 4 +--- Objects/genericaliasobject.c | 4 +--- Objects/genobject.c | 3 +-- Objects/methodobject.c | 4 +--- Objects/odictobject.c | 3 +-- Objects/picklebufobject.c | 3 +-- Objects/setobject.c | 3 +-- Objects/unionobject.c | 4 +--- 30 files changed, 32 insertions(+), 83 deletions(-) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index ad670293ec5b6a..d716b86fd308e5 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1532,9 +1532,7 @@ deque_dealloc(PyObject *self) Py_ssize_t i; PyObject_GC_UnTrack(deque); - if (deque->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); if (deque->leftblock != NULL) { (void)deque_clear(self); assert(deque->leftblock != NULL); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index fe24629f9f6f70..3aa6ba329e6968 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -690,8 +690,7 @@ element_dealloc(PyObject *op) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); /* element_gc_clear clears all references and deallocates extra */ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 354dbad84b5099..5fdad08a93eb6c 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -351,9 +351,7 @@ partial_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - if (partialobject_CAST(self)->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); (void)partial_clear(self); tp->tp_free(self); Py_DECREF(tp); @@ -1621,9 +1619,7 @@ lru_cache_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(obj); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(obj); - if (obj->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)lru_cache_tp_clear(op); tp->tp_free(obj); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index 4724e97982f349..f6342e7b05b146 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -421,8 +421,7 @@ buffered_dealloc(PyObject *op) return; _PyObject_GC_UNTRACK(self); self->ok = 0; - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; @@ -2312,8 +2311,7 @@ bufferedrwpair_dealloc(PyObject *op) rwpair *self = rwpair_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); (void)bufferedrwpair_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index 61cfec435fe60f..c11ba6d90ea4f4 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -979,8 +979,7 @@ bytesio_dealloc(PyObject *op) } Py_CLEAR(self->buf); Py_CLEAR(self->dict); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index 8fcb27049d6c7c..ef49d9ab29dedf 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -570,9 +570,7 @@ fileio_dealloc(PyObject *op) PyMem_Free(self->stat_atopen); self->stat_atopen = NULL; } - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)fileio_clear(op); PyTypeObject *tp = Py_TYPE(op); diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index cd4c7e7cead277..4ba8bc9552d0f2 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -383,8 +383,7 @@ iobase_dealloc(PyObject *op) } PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index 56913fafefba8b..cc4b577dfa9c0d 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -638,9 +638,7 @@ stringio_dealloc(PyObject *op) } PyUnicodeWriter_Discard(self->writer); (void)stringio_clear(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3808ecdceb9b70..3613fc61f70ffe 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -1469,8 +1469,7 @@ textiowrapper_dealloc(PyObject *op) return; self->ok = 0; _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); (void)textiowrapper_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index 3e783b9da45652..f4c22d0a8c6e75 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -518,8 +518,7 @@ winconsoleio_dealloc(PyObject *op) if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 3ee14b61b821d6..11cf6eae0ac2ff 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -221,9 +221,7 @@ simplequeue_dealloc(PyObject *op) PyObject_GC_UnTrack(self); (void)simplequeue_clear(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index 35d090e3ca2dce..adfa29e191eb41 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -56,9 +56,7 @@ blob_dealloc(PyObject *op) close_blob(self); - if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 7943bfcca3679d..73fb26eb3e73d7 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -185,9 +185,7 @@ cursor_dealloc(PyObject *op) pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - if (self->in_weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 602d0ab8588f62..103f68ec80874a 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -736,10 +736,7 @@ pattern_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PatternObject *obj = _PatternObject_CAST(self); - if (obj->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); (void)pattern_clear(self); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_struct.c b/Modules/_struct.c index c36079f1eb8886..149c012fd5c2f6 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -1794,9 +1794,7 @@ s_dealloc(PyObject *op) PyStructObject *s = PyStructObject_CAST(op); PyTypeObject *tp = Py_TYPE(s); PyObject_GC_UnTrack(s); - if (s->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); if (s->s_codes != NULL) { PyMem_Free(s->s_codes); } diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 257e0256655976..26d6c3eb5189db 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -1034,8 +1034,7 @@ heapctypewithweakref_dealloc(PyObject *op) { HeapCTypeWithWeakrefObject *self = (HeapCTypeWithWeakrefObject*)op; PyTypeObject *tp = Py_TYPE(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs((PyObject *) self); Py_XDECREF(self->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c index fc278a70b77d31..9bfb5eeee8e546 100644 --- a/Modules/_testlimitedcapi/heaptype_relative.c +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -297,9 +297,7 @@ heapctypewithrelativeweakref_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp); - if (data->weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); Py_XDECREF(data->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index abd53436b21b29..683f41c7677081 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -375,9 +375,7 @@ zoneinfo_dealloc(PyObject *obj_self) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(obj_self); - } + PyObject_ClearWeakRefs(obj_self); if (self->trans_list_utc != NULL) { PyMem_Free(self->trans_list_utc); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index 401a3a7072b846..d49cf208c29541 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -728,9 +728,7 @@ array_dealloc(PyObject *op) PyObject_GC_UnTrack(op); arrayobject *self = arrayobject_CAST(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); if (self->ob_item != NULL) { PyMem_Free(self->ob_item); } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index 7c4eb05488eb33..b2a00404812cd9 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -163,8 +163,7 @@ mmap_object_dealloc(PyObject *op) Py_END_ALLOW_THREADS #endif /* UNIX */ - if (m_obj->weakreflist != NULL) - PyObject_ClearWeakRefs(op); + PyObject_ClearWeakRefs(op); tp->tp_free(m_obj); Py_DECREF(tp); diff --git a/Objects/classobject.c b/Objects/classobject.c index 58e1d17977322e..613f879849c035 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -245,8 +245,7 @@ method_dealloc(PyObject *self) { PyMethodObject *im = _PyMethodObject_CAST(self); _PyObject_GC_UNTRACK(im); - if (im->im_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)im); + PyObject_ClearWeakRefs((PyObject *)im); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); assert(Py_IS_TYPE(self, &PyMethod_Type)); diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 3f53d4cfeb20ac..60acf3628c5b91 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -2436,9 +2436,7 @@ code_dealloc(PyObject *self) Py_XDECREF(co->_co_cached->_co_varnames); PyMem_Free(co->_co_cached); } - if (co->co_weakreflist != NULL) { - PyObject_ClearWeakRefs(self); - } + PyObject_ClearWeakRefs(self); free_monitoring_data(co->_co_monitoring); #ifdef Py_GIL_DISABLED // The first element always points to the mutable bytecode at the end of diff --git a/Objects/funcobject.c b/Objects/funcobject.c index f87b0e5d8f1e47..235352a8460378 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1148,9 +1148,7 @@ func_dealloc(PyObject *self) return; } _PyObject_GC_UNTRACK(op); - if (op->func_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *) op); - } + PyObject_ClearWeakRefs((PyObject *) op); (void)func_clear((PyObject*)op); // These aren't cleared by func_clear(). _Py_DECREF_CODE((PyCodeObject *)op->func_code); diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 07b57f0c552ce9..baa105d4eca0d2 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -33,9 +33,7 @@ ga_dealloc(PyObject *self) gaobject *alias = (gaobject *)self; _PyObject_GC_UNTRACK(self); - if (alias->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *)alias); - } + PyObject_ClearWeakRefs((PyObject *)alias); Py_XDECREF(alias->origin); Py_XDECREF(alias->args); Py_XDECREF(alias->parameters); diff --git a/Objects/genobject.c b/Objects/genobject.c index d0cb75d2d17b07..6c9595e40d50e3 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -161,8 +161,7 @@ gen_dealloc(PyObject *self) _PyObject_GC_UNTRACK(gen); - if (gen->gi_weakreflist != NULL) - PyObject_ClearWeakRefs(self); + PyObject_ClearWeakRefs(self); _PyObject_GC_TRACK(self); diff --git a/Objects/methodobject.c b/Objects/methodobject.c index c3dcd09ad1cdb6..e935c0895cf821 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -167,9 +167,7 @@ meth_dealloc(PyObject *self) { PyCFunctionObject *m = _PyCFunctionObject_CAST(self); PyObject_GC_UnTrack(m); - if (m->m_weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject*) m); - } + PyObject_ClearWeakRefs((PyObject*) m); // We need to access ml_flags here rather than later. // `m->m_ml` might have the same lifetime // as `m_self` when it's dynamically allocated. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 891f6197401503..09abf62f85b7e9 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1391,8 +1391,7 @@ odict_dealloc(PyObject *op) PyObject_GC_UnTrack(self); Py_XDECREF(self->od_inst_dict); - if (self->od_weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *)self); + PyObject_ClearWeakRefs((PyObject *)self); _odict_clear_nodes(self); PyDict_Type.tp_dealloc((PyObject *)self); diff --git a/Objects/picklebufobject.c b/Objects/picklebufobject.c index 3ce800de04c208..a073342dfc0cfc 100644 --- a/Objects/picklebufobject.c +++ b/Objects/picklebufobject.c @@ -111,8 +111,7 @@ picklebuf_dealloc(PyObject *op) { PyPickleBufferObject *self = (PyPickleBufferObject*)op; PyObject_GC_UnTrack(self); - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs((PyObject *) self); PyBuffer_Release(&self->view); Py_TYPE(self)->tp_free((PyObject *) self); } diff --git a/Objects/setobject.c b/Objects/setobject.c index 8aa6b0d180907b..fbbea11ce8d473 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -536,8 +536,7 @@ set_dealloc(PyObject *self) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(so); - if (so->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) so); + PyObject_ClearWeakRefs((PyObject *) so); for (entry = so->table; used > 0; entry++) { if (entry->key && entry->key != dummy) { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 00ca5b9bf80341..2108d31f63141f 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -21,9 +21,7 @@ unionobject_dealloc(PyObject *self) unionobject *alias = (unionobject *)self; _PyObject_GC_UNTRACK(self); - if (alias->weakreflist != NULL) { - PyObject_ClearWeakRefs((PyObject *)alias); - } + PyObject_ClearWeakRefs((PyObject *)alias); Py_XDECREF(alias->args); Py_XDECREF(alias->hashable_args); From df62ed5b5657e61de7384407217388736b262357 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Thu, 19 Jun 2025 21:20:16 +0800 Subject: [PATCH 13/15] update news --- .../2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst index 0acdf69ce95217..e21cb5ddf81dd1 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-06-17-22-34-58.gh-issue-135607.ucsLVu.rst @@ -1,2 +1,2 @@ -Fix potential :mod:`weakref` races on module objects on the :term:`free threaded ` build. From a176ac28d97009eb3f33b0d446c7dc8866f7eb13 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Fri, 20 Jun 2025 10:44:55 +0800 Subject: [PATCH 14/15] avoid cast --- Modules/_testcapi/heaptype.c | 2 +- Modules/_threadmodule.c | 4 +--- Objects/classobject.c | 2 +- Objects/funcobject.c | 2 +- Objects/genericaliasobject.c | 2 +- Objects/methodobject.c | 2 +- Objects/odictobject.c | 2 +- Objects/picklebufobject.c | 2 +- Objects/setobject.c | 2 +- Objects/unionobject.c | 2 +- 10 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index 26d6c3eb5189db..f81d2b2410e040 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -1034,7 +1034,7 @@ heapctypewithweakref_dealloc(PyObject *op) { HeapCTypeWithWeakrefObject *self = (HeapCTypeWithWeakrefObject*)op; PyTypeObject *tp = Py_TYPE(self); - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); Py_XDECREF(self->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 150a266b521736..43f2686d7e8979 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1365,9 +1365,7 @@ static void localdummy_dealloc(PyObject *op) { localdummyobject *self = localdummyobject_CAST(op); - if (self->weakreflist != NULL) { - PyObject_ClearWeakRefs(op); - } + PyObject_ClearWeakRefs(op); PyTypeObject *tp = Py_TYPE(self); tp->tp_free(self); Py_DECREF(tp); diff --git a/Objects/classobject.c b/Objects/classobject.c index 613f879849c035..6c165013e9e26a 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -245,7 +245,7 @@ method_dealloc(PyObject *self) { PyMethodObject *im = _PyMethodObject_CAST(self); _PyObject_GC_UNTRACK(im); - PyObject_ClearWeakRefs((PyObject *)im); + PyObject_ClearWeakRefs(self); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); assert(Py_IS_TYPE(self, &PyMethod_Type)); diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 235352a8460378..31940f7d9f5e34 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1148,7 +1148,7 @@ func_dealloc(PyObject *self) return; } _PyObject_GC_UNTRACK(op); - PyObject_ClearWeakRefs((PyObject *) op); + PyObject_ClearWeakRefs(self); (void)func_clear((PyObject*)op); // These aren't cleared by func_clear(). _Py_DECREF_CODE((PyCodeObject *)op->func_code); diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index baa105d4eca0d2..413ccff063b937 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -33,7 +33,7 @@ ga_dealloc(PyObject *self) gaobject *alias = (gaobject *)self; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs((PyObject *)alias); + PyObject_ClearWeakRefs(self); Py_XDECREF(alias->origin); Py_XDECREF(alias->args); Py_XDECREF(alias->parameters); diff --git a/Objects/methodobject.c b/Objects/methodobject.c index e935c0895cf821..fb5a175e833734 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -167,7 +167,7 @@ meth_dealloc(PyObject *self) { PyCFunctionObject *m = _PyCFunctionObject_CAST(self); PyObject_GC_UnTrack(m); - PyObject_ClearWeakRefs((PyObject*) m); + PyObject_ClearWeakRefs(self); // We need to access ml_flags here rather than later. // `m->m_ml` might have the same lifetime // as `m_self` when it's dynamically allocated. diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 09abf62f85b7e9..9018a481152619 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -1391,7 +1391,7 @@ odict_dealloc(PyObject *op) PyObject_GC_UnTrack(self); Py_XDECREF(self->od_inst_dict); - PyObject_ClearWeakRefs((PyObject *)self); + PyObject_ClearWeakRefs(op); _odict_clear_nodes(self); PyDict_Type.tp_dealloc((PyObject *)self); diff --git a/Objects/picklebufobject.c b/Objects/picklebufobject.c index a073342dfc0cfc..572100567b5230 100644 --- a/Objects/picklebufobject.c +++ b/Objects/picklebufobject.c @@ -111,7 +111,7 @@ picklebuf_dealloc(PyObject *op) { PyPickleBufferObject *self = (PyPickleBufferObject*)op; PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs((PyObject *) self); + PyObject_ClearWeakRefs(op); PyBuffer_Release(&self->view); Py_TYPE(self)->tp_free((PyObject *) self); } diff --git a/Objects/setobject.c b/Objects/setobject.c index fbbea11ce8d473..30581e4e4c7122 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -536,7 +536,7 @@ set_dealloc(PyObject *self) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(so); - PyObject_ClearWeakRefs((PyObject *) so); + PyObject_ClearWeakRefs(self); for (entry = so->table; used > 0; entry++) { if (entry->key && entry->key != dummy) { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 2108d31f63141f..191df9efbd6626 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -21,7 +21,7 @@ unionobject_dealloc(PyObject *self) unionobject *alias = (unionobject *)self; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs((PyObject *)alias); + PyObject_ClearWeakRefs(self); Py_XDECREF(alias->args); Py_XDECREF(alias->hashable_args); From 6feeca12b21ae3e3538e27fc48b05db674af1752 Mon Sep 17 00:00:00 2001 From: Xuanteng Huang Date: Fri, 20 Jun 2025 14:54:40 +0800 Subject: [PATCH 15/15] use macros to clear weak refs --- Include/internal/pycore_weakref.h | 10 ++++++++++ Modules/_collectionsmodule.c | 3 ++- Modules/_elementtree.c | 3 ++- Modules/_functoolsmodule.c | 5 +++-- Modules/_io/bufferedio.c | 5 +++-- Modules/_io/bytesio.c | 3 ++- Modules/_io/fileio.c | 3 ++- Modules/_io/iobase.c | 3 ++- Modules/_io/stringio.c | 3 ++- Modules/_io/textio.c | 3 ++- Modules/_io/winconsoleio.c | 3 ++- Modules/_queuemodule.c | 3 ++- Modules/_sqlite/blob.c | 3 ++- Modules/_sqlite/cursor.c | 3 ++- Modules/_sre/sre.c | 4 +++- Modules/_struct.c | 3 ++- Modules/_testcapi/heaptype.c | 3 ++- Modules/_testlimitedcapi/heaptype_relative.c | 4 +++- Modules/_zoneinfo.c | 3 ++- Modules/arraymodule.c | 3 ++- Modules/mmapmodule.c | 3 ++- Objects/classobject.c | 3 ++- Objects/codeobject.c | 3 ++- Objects/funcobject.c | 3 ++- Objects/genericaliasobject.c | 3 ++- Objects/genobject.c | 3 ++- Objects/methodobject.c | 3 ++- Objects/moduleobject.c | 3 ++- Objects/odictobject.c | 3 ++- Objects/picklebufobject.c | 3 ++- Objects/setobject.c | 3 ++- Objects/unionobject.c | 3 ++- 32 files changed, 76 insertions(+), 33 deletions(-) diff --git a/Include/internal/pycore_weakref.h b/Include/internal/pycore_weakref.h index 950aa0af290056..3b64b54db0364b 100644 --- a/Include/internal/pycore_weakref.h +++ b/Include/internal/pycore_weakref.h @@ -29,6 +29,9 @@ extern "C" { PyMutex_LockFlags(wr->weakrefs_lock, _Py_LOCK_DONT_DETACH) #define UNLOCK_WEAKREFS_FOR_WR(wr) PyMutex_Unlock(wr->weakrefs_lock) +#define FT_CLEAR_WEAKREFS(obj, weakref_list) \ + PyObject_ClearWeakRefs(obj) + #else #define LOCK_WEAKREFS(obj) @@ -37,6 +40,13 @@ extern "C" { #define LOCK_WEAKREFS_FOR_WR(wr) #define UNLOCK_WEAKREFS_FOR_WR(wr) +#define FT_CLEAR_WEAKREFS(obj, weakref_list) \ + do { \ + if (weakref_list != NULL) { \ + PyObject_ClearWeakRefs(obj); \ + } \ + } while (0) + #endif static inline int _is_dead(PyObject *obj) diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index d716b86fd308e5..e3522166ec2c94 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -5,6 +5,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_typeobject.h" // _PyType_GetModuleState() +#include "pycore_weakref.h" #include @@ -1532,7 +1533,7 @@ deque_dealloc(PyObject *self) Py_ssize_t i; PyObject_GC_UnTrack(deque); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, deque->weakreflist); if (deque->leftblock != NULL) { (void)deque_clear(self); assert(deque->leftblock != NULL); diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 3aa6ba329e6968..a16c51b4b0f8b2 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -17,6 +17,7 @@ #include "Python.h" #include "pycore_pyhash.h" // _Py_HashSecret +#include "pycore_weakref.h" #include // offsetof() #include "expat.h" @@ -690,7 +691,7 @@ element_dealloc(PyObject *op) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); /* element_gc_clear clears all references and deallocates extra */ diff --git a/Modules/_functoolsmodule.c b/Modules/_functoolsmodule.c index 5fdad08a93eb6c..67af8fd6c100e1 100644 --- a/Modules/_functoolsmodule.c +++ b/Modules/_functoolsmodule.c @@ -7,6 +7,7 @@ #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_tuple.h" // _PyTuple_ITEMS() +#include "pycore_weakref.h" #include "clinic/_functoolsmodule.c.h" @@ -351,7 +352,7 @@ partial_dealloc(PyObject *self) PyTypeObject *tp = Py_TYPE(self); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, partialobject_CAST(self)->weakreflist); (void)partial_clear(self); tp->tp_free(self); Py_DECREF(tp); @@ -1619,7 +1620,7 @@ lru_cache_dealloc(PyObject *op) PyTypeObject *tp = Py_TYPE(obj); /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(obj); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, obj->weakreflist); (void)lru_cache_tp_clear(op); tp->tp_free(obj); diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c index f6342e7b05b146..19c7921cfc4507 100644 --- a/Modules/_io/bufferedio.c +++ b/Modules/_io/bufferedio.c @@ -13,6 +13,7 @@ #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _Py_FatalErrorFormat() #include "pycore_pylifecycle.h" // _Py_IsInterpreterFinalizing() +#include "pycore_weakref.h" #include "_iomodule.h" @@ -421,7 +422,7 @@ buffered_dealloc(PyObject *op) return; _PyObject_GC_UNTRACK(self); self->ok = 0; - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); if (self->buffer) { PyMem_Free(self->buffer); self->buffer = NULL; @@ -2311,7 +2312,7 @@ bufferedrwpair_dealloc(PyObject *op) rwpair *self = rwpair_CAST(op); PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); (void)bufferedrwpair_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/bytesio.c b/Modules/_io/bytesio.c index c11ba6d90ea4f4..01b68a9bd1f110 100644 --- a/Modules/_io/bytesio.c +++ b/Modules/_io/bytesio.c @@ -3,6 +3,7 @@ #include "pycore_object.h" #include "pycore_pyatomic_ft_wrappers.h" #include "pycore_sysmodule.h" // _PySys_GetSizeOf() +#include "pycore_weakref.h" #include // offsetof() #include "_iomodule.h" @@ -979,7 +980,7 @@ bytesio_dealloc(PyObject *op) } Py_CLEAR(self->buf); Py_CLEAR(self->dict); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c index ef49d9ab29dedf..2b165150a85d94 100644 --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -4,6 +4,7 @@ #include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_weakref.h" #include // bool #ifdef HAVE_UNISTD_H @@ -570,7 +571,7 @@ fileio_dealloc(PyObject *op) PyMem_Free(self->stat_atopen); self->stat_atopen = NULL; } - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); (void)fileio_clear(op); PyTypeObject *tp = Py_TYPE(op); diff --git a/Modules/_io/iobase.c b/Modules/_io/iobase.c index 4ba8bc9552d0f2..10405bd010f2ab 100644 --- a/Modules/_io/iobase.c +++ b/Modules/_io/iobase.c @@ -14,6 +14,7 @@ #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_object.h" // _PyType_HasFeature() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_weakref.h" #include // offsetof() #include "_iomodule.h" @@ -383,7 +384,7 @@ iobase_dealloc(PyObject *op) } PyTypeObject *tp = Py_TYPE(self); _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c index cc4b577dfa9c0d..5160c734bccb23 100644 --- a/Modules/_io/stringio.c +++ b/Modules/_io/stringio.c @@ -1,6 +1,7 @@ #include "Python.h" #include // offsetof() #include "pycore_object.h" +#include "pycore_weakref.h" #include "_iomodule.h" /* Implementation note: the buffer is always at least one character longer @@ -638,7 +639,7 @@ stringio_dealloc(PyObject *op) } PyUnicodeWriter_Discard(self->writer); (void)stringio_clear(op); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c index 3613fc61f70ffe..f24303fd938737 100644 --- a/Modules/_io/textio.c +++ b/Modules/_io/textio.c @@ -16,6 +16,7 @@ #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_unicodeobject.h" // _PyUnicode_AsASCIIString() +#include "pycore_weakref.h" #include "_iomodule.h" @@ -1469,7 +1470,7 @@ textiowrapper_dealloc(PyObject *op) return; self->ok = 0; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); (void)textiowrapper_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_io/winconsoleio.c b/Modules/_io/winconsoleio.c index f4c22d0a8c6e75..708e24b8d8ceef 100644 --- a/Modules/_io/winconsoleio.c +++ b/Modules/_io/winconsoleio.c @@ -10,6 +10,7 @@ #include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH #include "pycore_object.h" // _PyObject_GC_UNTRACK() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() +#include "pycore_weakref.h" #ifdef HAVE_WINDOWS_CONSOLE_IO @@ -518,7 +519,7 @@ winconsoleio_dealloc(PyObject *op) if (_PyIOBase_finalize(op) < 0) return; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); Py_CLEAR(self->dict); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_queuemodule.c b/Modules/_queuemodule.c index 11cf6eae0ac2ff..aee07bddfc25da 100644 --- a/Modules/_queuemodule.c +++ b/Modules/_queuemodule.c @@ -7,6 +7,7 @@ #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_parking_lot.h" #include "pycore_time.h" // _PyTime_FromSecondsObject() +#include "pycore_weakref.h" #include #include // offsetof() @@ -221,7 +222,7 @@ simplequeue_dealloc(PyObject *op) PyObject_GC_UnTrack(self); (void)simplequeue_clear(op); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); tp->tp_free(self); Py_DECREF(tp); } diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c index adfa29e191eb41..f42fb11edc2e4d 100644 --- a/Modules/_sqlite/blob.c +++ b/Modules/_sqlite/blob.c @@ -4,6 +4,7 @@ #include "blob.h" #include "util.h" +#include "pycore_weakref.h" #define clinic_state() (pysqlite_get_state_by_type(Py_TYPE(self))) #include "clinic/blob.c.h" @@ -56,7 +57,7 @@ blob_dealloc(PyObject *op) close_blob(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->in_weakreflist); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c index 73fb26eb3e73d7..8e8c23c535cb58 100644 --- a/Modules/_sqlite/cursor.c +++ b/Modules/_sqlite/cursor.c @@ -31,6 +31,7 @@ #include "util.h" #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() +#include "pycore_weakref.h" typedef enum { TYPE_LONG, @@ -185,7 +186,7 @@ cursor_dealloc(PyObject *op) pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op); PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->in_weakreflist); (void)tp->tp_clear(op); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_sre/sre.c b/Modules/_sre/sre.c index 103f68ec80874a..ef27c39957ca57 100644 --- a/Modules/_sre/sre.c +++ b/Modules/_sre/sre.c @@ -44,6 +44,7 @@ static const char copyright[] = #include "pycore_long.h" // _PyLong_GetZero() #include "pycore_moduleobject.h" // _PyModule_GetState() #include "pycore_unicodeobject.h" // _PyUnicode_Copy +#include "pycore_weakref.h" #include "sre.h" // SRE_CODE @@ -736,7 +737,8 @@ pattern_dealloc(PyObject *self) { PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(self); + PatternObject *obj = _PatternObject_CAST(self); + FT_CLEAR_WEAKREFS(self, obj->weakreflist); (void)pattern_clear(self); tp->tp_free(self); Py_DECREF(tp); diff --git a/Modules/_struct.c b/Modules/_struct.c index 149c012fd5c2f6..37d68cd9cf376b 100644 --- a/Modules/_struct.c +++ b/Modules/_struct.c @@ -11,6 +11,7 @@ #include "pycore_bytesobject.h" // _PyBytesWriter #include "pycore_long.h" // _PyLong_AsByteArray() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_weakref.h" #include // offsetof() @@ -1794,7 +1795,7 @@ s_dealloc(PyObject *op) PyStructObject *s = PyStructObject_CAST(op); PyTypeObject *tp = Py_TYPE(s); PyObject_GC_UnTrack(s); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, s->weakreflist); if (s->s_codes != NULL) { PyMem_Free(s->s_codes); } diff --git a/Modules/_testcapi/heaptype.c b/Modules/_testcapi/heaptype.c index f81d2b2410e040..257e0256655976 100644 --- a/Modules/_testcapi/heaptype.c +++ b/Modules/_testcapi/heaptype.c @@ -1034,7 +1034,8 @@ heapctypewithweakref_dealloc(PyObject *op) { HeapCTypeWithWeakrefObject *self = (HeapCTypeWithWeakrefObject*)op; PyTypeObject *tp = Py_TYPE(self); - PyObject_ClearWeakRefs(op); + if (self->weakreflist != NULL) + PyObject_ClearWeakRefs((PyObject *) self); Py_XDECREF(self->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_testlimitedcapi/heaptype_relative.c b/Modules/_testlimitedcapi/heaptype_relative.c index 9bfb5eeee8e546..fc278a70b77d31 100644 --- a/Modules/_testlimitedcapi/heaptype_relative.c +++ b/Modules/_testlimitedcapi/heaptype_relative.c @@ -297,7 +297,9 @@ heapctypewithrelativeweakref_dealloc(PyObject* self) { PyTypeObject *tp = Py_TYPE(self); HeapCTypeWithWeakrefStruct *data = PyObject_GetTypeData(self, tp); - PyObject_ClearWeakRefs(self); + if (data->weakreflist != NULL) { + PyObject_ClearWeakRefs(self); + } Py_XDECREF(data->weakreflist); PyObject_Free(self); Py_DECREF(tp); diff --git a/Modules/_zoneinfo.c b/Modules/_zoneinfo.c index 683f41c7677081..6c0bef10e8577e 100644 --- a/Modules/_zoneinfo.c +++ b/Modules/_zoneinfo.c @@ -7,6 +7,7 @@ #include "pycore_long.h" // _PyLong_GetOne() #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_typeobject.h" // _PyType_GetModuleState() +#include "pycore_weakref.h" #include "datetime.h" // PyDateTime_TZInfo @@ -375,7 +376,7 @@ zoneinfo_dealloc(PyObject *obj_self) PyTypeObject *tp = Py_TYPE(self); PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(obj_self); + FT_CLEAR_WEAKREFS(obj_self, self->weakreflist); if (self->trans_list_utc != NULL) { PyMem_Free(self->trans_list_utc); diff --git a/Modules/arraymodule.c b/Modules/arraymodule.c index d49cf208c29541..492362874180d1 100644 --- a/Modules/arraymodule.c +++ b/Modules/arraymodule.c @@ -13,6 +13,7 @@ #include "pycore_ceval.h" // _PyEval_GetBuiltin() #include "pycore_modsupport.h" // _PyArg_NoKeywords() #include "pycore_moduleobject.h" // _PyModule_GetState() +#include "pycore_weakref.h" #include // offsetof() #include @@ -728,7 +729,7 @@ array_dealloc(PyObject *op) PyObject_GC_UnTrack(op); arrayobject *self = arrayobject_CAST(op); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); if (self->ob_item != NULL) { PyMem_Free(self->ob_item); } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c index b2a00404812cd9..a464083f42c048 100644 --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -25,6 +25,7 @@ #include #include "pycore_bytesobject.h" // _PyBytes_Find() #include "pycore_fileutils.h" // _Py_stat_struct +#include "pycore_weakref.h" #include // offsetof() #ifndef MS_WINDOWS @@ -163,7 +164,7 @@ mmap_object_dealloc(PyObject *op) Py_END_ALLOW_THREADS #endif /* UNIX */ - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, m_obj->weakreflist); tp->tp_free(m_obj); Py_DECREF(tp); diff --git a/Objects/classobject.c b/Objects/classobject.c index 6c165013e9e26a..cdae9f4a04776e 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -7,6 +7,7 @@ #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_weakref.h" #include "clinic/classobject.c.h" @@ -245,7 +246,7 @@ method_dealloc(PyObject *self) { PyMethodObject *im = _PyMethodObject_CAST(self); _PyObject_GC_UNTRACK(im); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, im->im_weakreflist); Py_DECREF(im->im_func); Py_XDECREF(im->im_self); assert(Py_IS_TYPE(self, &PyMethod_Type)); diff --git a/Objects/codeobject.c b/Objects/codeobject.c index 60acf3628c5b91..6deb847d1c93d9 100644 --- a/Objects/codeobject.c +++ b/Objects/codeobject.c @@ -17,6 +17,7 @@ #include "pycore_tuple.h" // _PyTuple_ITEMS() #include "pycore_unicodeobject.h" // _PyUnicode_InternImmortal() #include "pycore_uniqueid.h" // _PyObject_AssignUniqueId() +#include "pycore_weakref.h" #include "clinic/codeobject.c.h" #include @@ -2436,7 +2437,7 @@ code_dealloc(PyObject *self) Py_XDECREF(co->_co_cached->_co_varnames); PyMem_Free(co->_co_cached); } - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, co->co_weakreflist); free_monitoring_data(co->_co_monitoring); #ifdef Py_GIL_DISABLED // The first element always points to the mutable bytecode at the end of diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 31940f7d9f5e34..197cb472eba1e1 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -10,6 +10,7 @@ #include "pycore_pyerrors.h" // _PyErr_Occurred() #include "pycore_setobject.h" // _PySet_NextEntry() #include "pycore_stats.h" +#include "pycore_weakref.h" static const char * @@ -1148,7 +1149,7 @@ func_dealloc(PyObject *self) return; } _PyObject_GC_UNTRACK(op); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, op->func_weakreflist); (void)func_clear((PyObject*)op); // These aren't cleared by func_clear(). _Py_DECREF_CODE((PyCodeObject *)op->func_code); diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 413ccff063b937..e31fa794d86618 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -7,6 +7,7 @@ #include "pycore_typevarobject.h" // _Py_typing_type_repr #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() #include "pycore_unionobject.h" // _Py_union_type_or, _PyGenericAlias_Check +#include "pycore_weakref.h" #include @@ -33,7 +34,7 @@ ga_dealloc(PyObject *self) gaobject *alias = (gaobject *)self; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, alias->weakreflist); Py_XDECREF(alias->origin); Py_XDECREF(alias->args); Py_XDECREF(alias->parameters); diff --git a/Objects/genobject.c b/Objects/genobject.c index 6c9595e40d50e3..bc7e35962ef7b4 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -17,6 +17,7 @@ #include "pycore_pyerrors.h" // _PyErr_ClearExcState() #include "pycore_pystate.h" // _PyThreadState_GET() #include "pycore_warnings.h" // _PyErr_WarnUnawaitedCoroutine() +#include "pycore_weakref.h" #include "opcode_ids.h" // RESUME, etc @@ -161,7 +162,7 @@ gen_dealloc(PyObject *self) _PyObject_GC_UNTRACK(gen); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, gen->gi_weakreflist); _PyObject_GC_TRACK(self); diff --git a/Objects/methodobject.c b/Objects/methodobject.c index fb5a175e833734..bad51cb91dd8f5 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -8,6 +8,7 @@ #include "pycore_object.h" #include "pycore_pyerrors.h" #include "pycore_pystate.h" // _PyThreadState_GET() +#include "pycore_weakref.h" /* undefine macro trampoline to PyCFunction_NewEx */ @@ -167,7 +168,7 @@ meth_dealloc(PyObject *self) { PyCFunctionObject *m = _PyCFunctionObject_CAST(self); PyObject_GC_UnTrack(m); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, m->m_weakreflist); // We need to access ml_flags here rather than later. // `m->m_ml` might have the same lifetime // as `m_self` when it's dynamically allocated. diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c index be702d3f45287c..64c70dea524b61 100644 --- a/Objects/moduleobject.c +++ b/Objects/moduleobject.c @@ -13,6 +13,7 @@ #include "pycore_pyerrors.h" // _PyErr_FormatFromCause() #include "pycore_pystate.h" // _PyInterpreterState_GET() #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString() +#include "pycore_weakref.h" #include "osdefs.h" // MAXPATHLEN @@ -826,7 +827,7 @@ module_dealloc(PyObject *self) if (verbose && m->md_name) { PySys_FormatStderr("# destroy %U\n", m->md_name); } - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, m->md_weaklist); /* bpo-39824: Don't call m_free() if m_size > 0 and md_state=NULL */ if (m->md_def && m->md_def->m_free diff --git a/Objects/odictobject.c b/Objects/odictobject.c index 9018a481152619..0eb89d71491bfb 100644 --- a/Objects/odictobject.c +++ b/Objects/odictobject.c @@ -473,6 +473,7 @@ Potential Optimizations #include "pycore_pyerrors.h" // _PyErr_ChainExceptions1() #include "pycore_tuple.h" // _PyTuple_Recycle() #include // offsetof() +#include "pycore_weakref.h" #include "clinic/odictobject.c.h" @@ -1391,7 +1392,7 @@ odict_dealloc(PyObject *op) PyObject_GC_UnTrack(self); Py_XDECREF(self->od_inst_dict); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->od_weakreflist); _odict_clear_nodes(self); PyDict_Type.tp_dealloc((PyObject *)self); diff --git a/Objects/picklebufobject.c b/Objects/picklebufobject.c index 572100567b5230..377e35715dd85f 100644 --- a/Objects/picklebufobject.c +++ b/Objects/picklebufobject.c @@ -1,6 +1,7 @@ /* PickleBuffer object implementation */ #include "Python.h" +#include "pycore_weakref.h" #include typedef struct { @@ -111,7 +112,7 @@ picklebuf_dealloc(PyObject *op) { PyPickleBufferObject *self = (PyPickleBufferObject*)op; PyObject_GC_UnTrack(self); - PyObject_ClearWeakRefs(op); + FT_CLEAR_WEAKREFS(op, self->weakreflist); PyBuffer_Release(&self->view); Py_TYPE(self)->tp_free((PyObject *) self); } diff --git a/Objects/setobject.c b/Objects/setobject.c index 30581e4e4c7122..ea4c17ab19f802 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -40,6 +40,7 @@ #include "pycore_pyatomic_ft_wrappers.h" // FT_ATOMIC_LOAD_SSIZE_RELAXED() #include "pycore_pyerrors.h" // _PyErr_SetKeyError() #include "pycore_setobject.h" // _PySet_NextEntry() definition +#include "pycore_weakref.h" #include "stringlib/eq.h" // unicode_eq() #include // offsetof() @@ -536,7 +537,7 @@ set_dealloc(PyObject *self) /* bpo-31095: UnTrack is needed before calling any callbacks */ PyObject_GC_UnTrack(so); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, so->weakreflist); for (entry = so->table; used > 0; entry++) { if (entry->key && entry->key != dummy) { diff --git a/Objects/unionobject.c b/Objects/unionobject.c index 191df9efbd6626..211922deef735a 100644 --- a/Objects/unionobject.c +++ b/Objects/unionobject.c @@ -4,6 +4,7 @@ #include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_typing_type_repr #include "pycore_unicodeobject.h" // _PyUnicode_EqualToASCIIString #include "pycore_unionobject.h" +#include "pycore_weakref.h" typedef struct { @@ -21,7 +22,7 @@ unionobject_dealloc(PyObject *self) unionobject *alias = (unionobject *)self; _PyObject_GC_UNTRACK(self); - PyObject_ClearWeakRefs(self); + FT_CLEAR_WEAKREFS(self, alias->weakreflist); Py_XDECREF(alias->args); Py_XDECREF(alias->hashable_args);