Skip to content

Commit d138c4d

Browse files
committed
Don't clear weakref if wr_object is a type.
1 parent c419af9 commit d138c4d

1 file changed

Lines changed: 25 additions & 9 deletions

File tree

Python/gc.c

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,19 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
858858
}
859859
}
860860

861+
static bool
862+
should_clear_weakref(PyWeakReference *wr)
863+
{
864+
if (wr->wr_callback == NULL && PyType_Check(wr->wr_object)) {
865+
/* This is intended to match the values of the tp_subclasses dict.
866+
* It matches weakrefs to types in other places as well, which is
867+
* an unintended side effect.
868+
*/
869+
return false;
870+
}
871+
return true;
872+
}
873+
861874
/* Clear all weakrefs to unreachable objects, and if such a weakref has a
862875
* callback, invoke it if necessary. Note that it's possible for such
863876
* weakrefs to be outside the unreachable set -- indeed, those are precisely
@@ -895,7 +908,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
895908
op = FROM_GC(gc);
896909
next = GC_NEXT(gc);
897910

898-
if (PyWeakref_Check(op)) {
911+
if (PyWeakref_Check(op) && should_clear_weakref((PyWeakReference *)op)) {
899912
/* A weakref inside the unreachable set must be cleared. If we
900913
* allow its callback to execute inside delete_garbage(), it
901914
* could expose objects that have tp_clear already called on
@@ -925,16 +938,19 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
925938
* all the weakrefs, and move the weakrefs with callbacks
926939
* that must be called into wrcb_to_call.
927940
*/
928-
for (wr = *wrlist; wr != NULL; wr = *wrlist) {
941+
PyWeakReference *next_wr;
942+
for (wr = *wrlist; wr != NULL; wr = next_wr) {
943+
next_wr = wr->wr_next;
929944
PyGC_Head *wrasgc; /* AS_GC(wr) */
930945

931-
/* _PyWeakref_ClearRef clears the weakref but leaves
932-
* the callback pointer intact. Obscure: it also
933-
* changes *wrlist.
934-
*/
935-
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
936-
_PyWeakref_ClearRef(wr);
937-
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
946+
if (should_clear_weakref(wr)) {
947+
/* _PyWeakref_ClearRef clears the weakref but leaves
948+
* the callback pointer intact. Obscure: it also
949+
* changes *wrlist.
950+
*/
951+
_PyWeakref_ClearRef(wr);
952+
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
953+
}
938954
if (wr->wr_callback == NULL) {
939955
/* no callback */
940956
continue;

0 commit comments

Comments
 (0)