mirror of
https://github.com/python/cpython.git
synced 2025-12-31 04:23:37 +00:00
gh-142975: During GC, mark frozen objects with a merged zero refcount for destruction (GH-143156)
This commit is contained in:
parent
579c5b496b
commit
8611f74e08
3 changed files with 39 additions and 1 deletions
|
|
@ -62,6 +62,38 @@ def mutator_thread():
|
|||
with threading_helper.start_threads(gcs + mutators):
|
||||
pass
|
||||
|
||||
def test_freeze_object_in_brc_queue(self):
|
||||
# GH-142975: Freezing objects in the BRC queue could result in some
|
||||
# objects having a zero refcount without being deallocated.
|
||||
|
||||
class Weird:
|
||||
# We need a destructor to trigger the check for object resurrection
|
||||
def __del__(self):
|
||||
pass
|
||||
|
||||
# This is owned by the main thread, so the subthread will have to increment
|
||||
# this object's reference count.
|
||||
weird = Weird()
|
||||
|
||||
def evil():
|
||||
gc.freeze()
|
||||
|
||||
# Decrement the reference count from this thread, which will trigger the
|
||||
# slow path during resurrection and add our weird object to the BRC queue.
|
||||
nonlocal weird
|
||||
del weird
|
||||
|
||||
# Collection will merge the object's reference count and make it zero.
|
||||
gc.collect()
|
||||
|
||||
# Unfreeze the object, making it visible to the GC.
|
||||
gc.unfreeze()
|
||||
gc.collect()
|
||||
|
||||
thread = Thread(target=evil)
|
||||
thread.start()
|
||||
thread.join()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
Fix crash after unfreezing all objects tracked by the garbage collector on
|
||||
the :term:`free threaded <free threading>` build.
|
||||
|
|
@ -906,7 +906,11 @@ gc_visit_thread_stacks_mark_alive(PyInterpreterState *interp, gc_mark_args_t *ar
|
|||
static void
|
||||
queue_untracked_obj_decref(PyObject *op, struct collection_state *state)
|
||||
{
|
||||
if (!_PyObject_GC_IS_TRACKED(op)) {
|
||||
assert(Py_REFCNT(op) == 0);
|
||||
// gh-142975: We have to treat frozen objects as untracked in this function
|
||||
// or else they might be picked up in a future collection, which breaks the
|
||||
// assumption that all incoming objects have a non-zero reference count.
|
||||
if (!_PyObject_GC_IS_TRACKED(op) || gc_is_frozen(op)) {
|
||||
// GC objects with zero refcount are handled subsequently by the
|
||||
// GC as if they were cyclic trash, but we have to handle dead
|
||||
// non-GC objects here. Add one to the refcount so that we can
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue