Skip to content

Commit 7642070

Browse files
authored
[3.14] Revert "GH-91636: Clear weakrefs created by finalizers. (GH-136401) (#141993)" (#142152)
1 parent 4ce2790 commit 7642070

File tree

4 files changed

+14
-54
lines changed

4 files changed

+14
-54
lines changed

Lib/test/test_gc.py

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -262,11 +262,9 @@ class Cyclic(tuple):
262262
# finalizer.
263263
def __del__(self):
264264
265-
# 5. Create a weakref to `func` now. In previous
266-
# versions of Python, this would avoid having it
267-
# cleared by the garbage collector before calling
268-
# the finalizers. Now, weakrefs get cleared after
269-
# calling finalizers.
265+
# 5. Create a weakref to `func` now. If we had created
266+
# it earlier, it would have been cleared by the
267+
# garbage collector before calling the finalizers.
270268
self[1].ref = weakref.ref(self[0])
271269
272270
# 6. Drop the global reference to `latefin`. The only
@@ -295,18 +293,14 @@ def func():
295293
# which will find `cyc` and `func` as garbage.
296294
gc.collect()
297295
298-
# 9. Previously, this would crash because the weakref
299-
# created in the finalizer revealed the function after
300-
# `tp_clear` was called and `func_qualname`
301-
# had been NULL-ed out by func_clear(). Now, we clear
302-
# weakrefs to unreachable objects before calling `tp_clear`
303-
# but after calling finalizers.
296+
# 9. Previously, this would crash because `func_qualname`
297+
# had been NULL-ed out by func_clear().
304298
print(f"{func=}")
305299
"""
300+
# We're mostly just checking that this doesn't crash.
306301
rc, stdout, stderr = assert_python_ok("-c", code)
307302
self.assertEqual(rc, 0)
308-
# The `func` global is None because the weakref was cleared.
309-
self.assertRegex(stdout, rb"""\A\s*func=None""")
303+
self.assertRegex(stdout, rb"""\A\s*func=<function at \S+>\s*\z""")
310304
self.assertFalse(stderr)
311305

312306
@refcount_test

Misc/NEWS.d/next/Core_and_Builtins/2025-07-07-17-26-06.gh-issue-91636.GyHU72.rst

Lines changed: 0 additions & 3 deletions
This file was deleted.

Python/gc.c

Lines changed: 3 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -870,7 +870,7 @@ move_legacy_finalizer_reachable(PyGC_Head *finalizers)
870870
* no object in `unreachable` is weakly referenced anymore.
871871
*/
872872
static int
873-
handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks)
873+
handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
874874
{
875875
PyGC_Head *gc;
876876
PyObject *op; /* generally FROM_GC(gc) */
@@ -879,9 +879,7 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks)
879879
PyGC_Head *next;
880880
int num_freed = 0;
881881

882-
if (allow_callbacks) {
883-
gc_list_init(&wrcb_to_call);
884-
}
882+
gc_list_init(&wrcb_to_call);
885883

886884
/* Clear all weakrefs to the objects in unreachable. If such a weakref
887885
* also has a callback, move it into `wrcb_to_call` if the callback
@@ -937,11 +935,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks)
937935
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == op);
938936
_PyWeakref_ClearRef(wr);
939937
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
940-
941-
if (!allow_callbacks) {
942-
continue;
943-
}
944-
945938
if (wr->wr_callback == NULL) {
946939
/* no callback */
947940
continue;
@@ -994,10 +987,6 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old, bool allow_callbacks)
994987
}
995988
}
996989

997-
if (!allow_callbacks) {
998-
return 0;
999-
}
1000-
1001990
/* Invoke the callbacks we decided to honor. It's safe to invoke them
1002991
* because they can't reference unreachable objects.
1003992
*/
@@ -1750,7 +1739,7 @@ gc_collect_region(PyThreadState *tstate,
17501739
}
17511740

17521741
/* Clear weakrefs and invoke callbacks as necessary. */
1753-
stats->collected += handle_weakrefs(&unreachable, to, true);
1742+
stats->collected += handle_weakrefs(&unreachable, to);
17541743
gc_list_validate_space(to, gcstate->visited_space);
17551744
validate_list(to, collecting_clear_unreachable_clear);
17561745
validate_list(&unreachable, collecting_set_unreachable_clear);
@@ -1764,14 +1753,6 @@ gc_collect_region(PyThreadState *tstate,
17641753
gc_list_init(&final_unreachable);
17651754
handle_resurrected_objects(&unreachable, &final_unreachable, to);
17661755

1767-
/* Clear weakrefs to objects in the unreachable set. No Python-level
1768-
* code must be allowed to access those unreachable objects. During
1769-
* delete_garbage(), finalizers outside the unreachable set might run
1770-
* and create new weakrefs. If those weakrefs were not cleared, they
1771-
* could reveal unreachable objects. Callbacks are not executed.
1772-
*/
1773-
handle_weakrefs(&final_unreachable, NULL, false);
1774-
17751756
/* Call tp_clear on objects in the final_unreachable set. This will cause
17761757
* the reference cycles to be broken. It may also cause some objects
17771758
* in finalizers to be freed.

Python/gc_free_threading.c

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,9 +1493,9 @@ move_legacy_finalizer_reachable(struct collection_state *state)
14931493
}
14941494

14951495
// Clear all weakrefs to unreachable objects. Weakrefs with callbacks are
1496-
// optionally enqueued in `wrcb_to_call`, but not invoked yet.
1496+
// enqueued in `wrcb_to_call`, but not invoked yet.
14971497
static void
1498-
clear_weakrefs(struct collection_state *state, bool enqueue_callbacks)
1498+
clear_weakrefs(struct collection_state *state)
14991499
{
15001500
PyObject *op;
15011501
WORKSTACK_FOR_EACH(&state->unreachable, op) {
@@ -1527,10 +1527,6 @@ clear_weakrefs(struct collection_state *state, bool enqueue_callbacks)
15271527
_PyWeakref_ClearRef(wr);
15281528
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
15291529

1530-
if (!enqueue_callbacks) {
1531-
continue;
1532-
}
1533-
15341530
// We do not invoke callbacks for weakrefs that are themselves
15351531
// unreachable. This is partly for historical reasons: weakrefs
15361532
// predate safe object finalization, and a weakref that is itself
@@ -2216,7 +2212,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
22162212
interp->gc.long_lived_total = state->long_lived_total;
22172213

22182214
// Clear weakrefs and enqueue callbacks (but do not call them).
2219-
clear_weakrefs(state, true);
2215+
clear_weakrefs(state);
22202216
_PyEval_StartTheWorld(interp);
22212217

22222218
// Deallocate any object from the refcount merge step
@@ -2227,19 +2223,11 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
22272223
call_weakref_callbacks(state);
22282224
finalize_garbage(state);
22292225

2230-
_PyEval_StopTheWorld(interp);
22312226
// Handle any objects that may have resurrected after the finalization.
2227+
_PyEval_StopTheWorld(interp);
22322228
err = handle_resurrected_objects(state);
22332229
// Clear free lists in all threads
22342230
_PyGC_ClearAllFreeLists(interp);
2235-
if (err == 0) {
2236-
// Clear weakrefs to objects in the unreachable set. No Python-level
2237-
// code must be allowed to access those unreachable objects. During
2238-
// delete_garbage(), finalizers outside the unreachable set might
2239-
// run and create new weakrefs. If those weakrefs were not cleared,
2240-
// they could reveal unreachable objects.
2241-
clear_weakrefs(state, false);
2242-
}
22432231
_PyEval_StartTheWorld(interp);
22442232

22452233
if (err < 0) {

0 commit comments

Comments
 (0)