MCPcopy Index your code
hub / github.com/python/cpython / test_bug1055820d

Method test_bug1055820d

Lib/test/test_gc.py:1530–1599  ·  view source on GitHub ↗
(self)

Source from the content-addressed store, hash-verified

1528 @gc_threshold(1000, 0, 0)
1529 @unittest.skipIf(Py_GIL_DISABLED, "requires GC generations or increments")
1530 def test_bug1055820d(self):
1531 # Corresponds to temp2d.py in the bug report. This is very much like
1532 # test_bug1055820c, but uses a __del__ method instead of a weakref
1533 # callback to sneak in a resurrection of cyclic trash.
1534
1535 ouch = []
1536 class D(C1055820):
1537 def __del__(self):
1538 ouch[:] = [c2wr()]
1539
1540 d0 = D(0)
1541 # Move all the above into generation 2.
1542 gc.collect()
1543
1544 c1 = C1055820(1)
1545 c1.keep_d0_alive = d0
1546 del d0.loop # now only c1 keeps d0 alive
1547
1548 c2 = C1055820(2)
1549 c2wr = weakref.ref(c2) # no callback!
1550
1551 d0 = c1 = c2 = None
1552
1553 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
1554 # generation 2. The only thing keeping it alive is that c1 points to
1555 # it. c1 and c2 are in generation 0, and are in self-loops. There's
1556 # a global weakref to c2 (c2wr), but that weakref has no callback.
1557 # There are no other weakrefs.
1558 #
1559 # d0 has a __del__ method that references c2wr
1560 # ^
1561 # |
1562 # | Generation 2 above dots
1563 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
1564 # | Generation 0 below dots
1565 # |
1566 # |
1567 # ^->c1 ^->c2 has a wr but no callback
1568 # | | | |
1569 # <--v <--v
1570 #
1571 # So this is the nightmare: when generation 0 gets collected, we see
1572 # that c2 has a callback-free weakref, and c1 doesn't even have a
1573 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
1574 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
1575 # on d0 to 0, so d0 goes away (despite that it's in an older
1576 # generation) and d0's __del__ triggers. That in turn materializes
1577 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
1578
1579 # We want to let gc happen "naturally", to preserve the distinction
1580 # between generations.
1581 detector = GC_Detector()
1582 junk = []
1583 i = 0
1584 if Py_GIL_DISABLED:
1585 # The free-threaded build doesn't have multiple generations, so
1586 # just trigger a GC manually.
1587 gc.collect()

Callers

nothing calls this directly

Calls 7

C1055820Class · 0.85
GC_DetectorClass · 0.85
DClass · 0.70
collectMethod · 0.45
failMethod · 0.45
assertEqualMethod · 0.45
appendMethod · 0.45

Tested by

no test coverage detected