aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Python/gc_free_threading.c
diff options
context:
space:
mode:
Diffstat (limited to 'Python/gc_free_threading.c')
-rw-r--r--Python/gc_free_threading.c20
1 files changed, 16 insertions, 4 deletions
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 5aaa68c5b51..d46598b23b3 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -1492,9 +1492,9 @@ move_legacy_finalizer_reachable(struct collection_state *state)
}
// Clear all weakrefs to unreachable objects. Weakrefs with callbacks are
-// enqueued in `wrcb_to_call`, but not invoked yet.
+// optionally enqueued in `wrcb_to_call`, but not invoked yet.
static void
-clear_weakrefs(struct collection_state *state)
+clear_weakrefs(struct collection_state *state, bool enqueue_callbacks)
{
PyObject *op;
WORKSTACK_FOR_EACH(&state->unreachable, op) {
@@ -1526,6 +1526,10 @@ clear_weakrefs(struct collection_state *state)
_PyWeakref_ClearRef(wr);
_PyObject_ASSERT((PyObject *)wr, wr->wr_object == Py_None);
+ if (!enqueue_callbacks) {
+ continue;
+ }
+
// We do not invoke callbacks for weakrefs that are themselves
// unreachable. This is partly for historical reasons: weakrefs
// predate safe object finalization, and a weakref that is itself
@@ -2211,7 +2215,7 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
interp->gc.long_lived_total = state->long_lived_total;
// Clear weakrefs and enqueue callbacks (but do not call them).
- clear_weakrefs(state);
+ clear_weakrefs(state, true);
_PyEval_StartTheWorld(interp);
// Deallocate any object from the refcount merge step
@@ -2222,11 +2226,19 @@ gc_collect_internal(PyInterpreterState *interp, struct collection_state *state,
call_weakref_callbacks(state);
finalize_garbage(state);
- // Handle any objects that may have resurrected after the finalization.
_PyEval_StopTheWorld(interp);
+ // Handle any objects that may have resurrected after the finalization.
err = handle_resurrected_objects(state);
// Clear free lists in all threads
_PyGC_ClearAllFreeLists(interp);
+ if (err == 0) {
+ // Clear weakrefs to objects in the unreachable set. No Python-level
+ // code must be allowed to access those unreachable objects. During
+ // delete_garbage(), finalizers outside the unreachable set might
+ // run and create new weakrefs. If those weakrefs were not cleared,
+ // they could reveal unreachable objects.
+ clear_weakrefs(state, false);
+ }
_PyEval_StartTheWorld(interp);
if (err < 0) {