Skip to content

Commit 123bc25

Browse files
committed
Ensure weakrefs with callbacks are cleared early.
We need to clear those before executing the callback. Since this ensures they can't run a second time, we don't need _PyGC_SET_FINALIZED(). Revise comments.
1 parent 2f3daba commit 123bc25

File tree

2 files changed

+46
-50
lines changed

2 files changed

+46
-50
lines changed

Python/gc.c

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -901,23 +901,30 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old)
901901
for (PyWeakReference *wr = *wrlist; wr != NULL; wr = next_wr) {
902902
next_wr = wr->wr_next;
903903

904-
// Since Python 2.3, weakrefs to cyclic garbage have been cleared
905-
// *before* calling finalizers. However, since tp_subclasses
906-
// started being necessary to invalidate caches (e.g. by
907-
// PyType_Modified()), that clearing has created a bug. If the
908-
// weakref to the subclass is cleared before a finalizer is
909-
// called, the cache may not be correctly invalidated. That can
910-
// lead to segfaults since the caches can refer to deallocated
904+
// Weakrefs with callbacks always need to be cleared before
905+
// executing the callback. Sometimes the callback will call
906+
// the ref object, to check if it's actually a dead reference
907+
// (KeyedRef does this, for example). We want to indicate that it
908+
// is dead, even though it is possible a finalizer might resurrect
909+
// it. Clearing also prevents the callback from being executing
910+
// more than once.
911+
//
912+
// Since Python 2.3, all weakrefs to cyclic garbage have
913+
// been cleared *before* calling finalizers. However, since
914+
// tp_subclasses started being necessary to invalidate caches
915+
// (e.g. by PyType_Modified()), that clearing has created a bug.
916+
// If the weakref to the subclass is cleared before a finalizer
917+
// is called, the cache may not be correctly invalidated. That
918+
// can lead to segfaults since the caches can refer to deallocated
911919
// objects. Delaying the clear of weakrefs until *after*
912-
// finalizers have been called fixes that bug. However, that can
913-
// introduce other problems since some finalizer code expects that
914-
// the weakrefs will be cleared first. The "multiprocessing"
915-
// package contains finalizer logic like this, for example. So,
916-
// we have the PyType_Check() test above and only defer the clear
917-
// of types. That solves the issue for tp_subclasses. In a
918-
// future version of Python, we should likely defer the weakref
919-
// clear for all objects, not just types.
920-
if (!PyType_Check(wr->wr_object)) {
920+
// finalizers have been called fixes that bug. However, that
921+
// deferral could introduce other problems if some finalizer
922+
// code expects that the weakrefs will be cleared first. So, we
923+
// have the PyType_Check() test below to only defer the clear of
924+
// weakrefs to types. That solves the issue for tp_subclasses.
925+
// In a future version of Python, we should likely defer the
926+
// weakref clear for all objects, not just types.
927+
if (wr->wr_callback != NULL || !PyType_Check(wr->wr_object)) {
921928
// _PyWeakref_ClearRef clears the weakref but leaves the
922929
// callback pointer intact. Obscure: it also changes *wrlist.
923930
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
@@ -962,11 +969,6 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old)
962969
continue;
963970
}
964971

965-
if (_PyGC_FINALIZED((PyObject *)wr)) {
966-
/* Callback was already run (weakref must have been resurrected). */
967-
continue;
968-
}
969-
970972
/* Create a new reference so that wr can't go away
971973
* before we can process it again.
972974
*/
@@ -995,10 +997,6 @@ handle_weakref_callbacks(PyGC_Head *unreachable, PyGC_Head *old)
995997
callback = wr->wr_callback;
996998
_PyObject_ASSERT(op, callback != NULL);
997999

998-
/* Ensure we don't execute the callback again if the weakref is
999-
* resurrected. */
1000-
_PyGC_SET_FINALIZED(op);
1001-
10021000
/* copy-paste of weakrefobject.c's handle_callback() */
10031001
temp = PyObject_CallOneArg(callback, (PyObject *)wr);
10041002
if (temp == NULL) {

Python/gc_free_threading.c

Lines changed: 23 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,23 +1513,30 @@ find_weakref_callbacks(struct collection_state *state)
15131513
for (PyWeakReference *wr = *wrlist; wr != NULL; wr = next_wr) {
15141514
next_wr = wr->wr_next;
15151515

1516-
// Since Python 2.3, weakrefs to cyclic garbage have been cleared
1517-
// *before* calling finalizers. However, since tp_subclasses
1518-
// started being necessary to invalidate caches (e.g. by
1519-
// PyType_Modified()), that clearing has created a bug. If the
1520-
// weakref to the subclass is cleared before a finalizer is
1521-
// called, the cache may not be correctly invalidated. That can
1522-
// lead to segfaults since the caches can refer to deallocated
1516+
// Weakrefs with callbacks always need to be cleared before
1517+
// executing the callback. Sometimes the callback will call
1518+
// the ref object, to check if it's actually a dead reference
1519+
// (KeyedRef does this, for example). We want to indicate that it
1520+
// is dead, even though it is possible a finalizer might resurrect
1521+
// it. Clearing also prevents the callback from being executing
1522+
// more than once.
1523+
//
1524+
// Since Python 2.3, all weakrefs to cyclic garbage have
1525+
// been cleared *before* calling finalizers. However, since
1526+
// tp_subclasses started being necessary to invalidate caches
1527+
// (e.g. by PyType_Modified()), that clearing has created a bug.
1528+
// If the weakref to the subclass is cleared before a finalizer
1529+
// is called, the cache may not be correctly invalidated. That
1530+
// can lead to segfaults since the caches can refer to deallocated
15231531
// objects. Delaying the clear of weakrefs until *after*
1524-
// finalizers have been called fixes that bug. However, that can
1525-
// introduce other problems since some finalizer code expects that
1526-
// the weakrefs will be cleared first. The "multiprocessing"
1527-
// package contains finalizer logic like this, for example. So,
1528-
// we have the PyType_Check() test above and only defer the clear
1529-
// of types. That solves the issue for tp_subclasses. In a
1530-
// future version of Python, we should likely defer the weakref
1531-
// clear for all objects, not just types.
1532-
if (!PyType_Check(wr->wr_object)) {
1532+
// finalizers have been called fixes that bug. However, that
1533+
// deferral could introduce other problems if some finalizer
1534+
// code expects that the weakrefs will be cleared first. So, we
1535+
// have the PyType_Check() test below to only defer the clear of
1536+
// weakrefs to types. That solves the issue for tp_subclasses.
1537+
// In a future version of Python, we should likely defer the
1538+
// weakref clear for all objects, not just types.
1539+
if (wr->wr_callback != NULL || !PyType_Check(wr->wr_object)) {
15331540
// _PyWeakref_ClearRef clears the weakref but leaves the
15341541
// callback pointer intact. Obscure: it also changes *wrlist.
15351542
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
@@ -1546,11 +1553,6 @@ find_weakref_callbacks(struct collection_state *state)
15461553
continue;
15471554
}
15481555

1549-
if (_PyGC_FINALIZED((PyObject *)wr)) {
1550-
// Callback was already run (weakref must have been resurrected).
1551-
continue;
1552-
}
1553-
15541556
// Create a new reference so that wr can't go away before we can
15551557
// process it again.
15561558
merge_refcount((PyObject *)wr, 1);
@@ -1610,10 +1612,6 @@ call_weakref_callbacks(struct collection_state *state)
16101612
PyObject *callback = wr->wr_callback;
16111613
_PyObject_ASSERT(op, callback != NULL);
16121614

1613-
/* Ensure we don't execute the callback again if the weakref is
1614-
* resurrected. */
1615-
_PyGC_SET_FINALIZED(op);
1616-
16171615
/* copy-paste of weakrefobject.c's handle_callback() */
16181616
PyObject *temp = PyObject_CallOneArg(callback, (PyObject *)wr);
16191617
if (temp == NULL) {

0 commit comments

Comments
 (0)