Back to index

python3.2  3.2.2
Public Member Functions
test.test_gc.GCTogglingTests Class Reference

List of all members.

Public Member Functions

def setUp
def tearDown
def test_bug1055820c
def test_bug1055820d

Detailed Description

Definition at line 527 of file test_gc.py.


Member Function Documentation

Definition at line 528 of file test_gc.py.

00528 
00529     def setUp(self):
00530         gc.enable()

Here is the caller graph for this function:

Definition at line 531 of file test_gc.py.

00531 
00532     def tearDown(self):
00533         gc.disable()

Here is the caller graph for this function:

Definition at line 534 of file test_gc.py.

00534 
00535     def test_bug1055820c(self):
00536         # Corresponds to temp2c.py in the bug report.  This is pretty
00537         # elaborate.
00538 
00539         c0 = C1055820(0)
00540         # Move c0 into generation 2.
00541         gc.collect()
00542 
00543         c1 = C1055820(1)
00544         c1.keep_c0_alive = c0
00545         del c0.loop # now only c1 keeps c0 alive
00546 
00547         c2 = C1055820(2)
00548         c2wr = weakref.ref(c2) # no callback!
00549 
00550         ouch = []
00551         def callback(ignored):
00552             ouch[:] = [c2wr()]
00553 
00554         # The callback gets associated with a wr on an object in generation 2.
00555         c0wr = weakref.ref(c0, callback)
00556 
00557         c0 = c1 = c2 = None
00558 
00559         # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
00560         # generation 2.  The only thing keeping it alive is that c1 points to
00561         # it. c1 and c2 are in generation 0, and are in self-loops.  There's a
00562         # global weakref to c2 (c2wr), but that weakref has no callback.
00563         # There's also a global weakref to c0 (c0wr), and that does have a
00564         # callback, and that callback references c2 via c2wr().
00565         #
00566         #               c0 has a wr with callback, which references c2wr
00567         #               ^
00568         #               |
00569         #               |     Generation 2 above dots
00570         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
00571         #               |     Generation 0 below dots
00572         #               |
00573         #               |
00574         #            ^->c1   ^->c2 has a wr but no callback
00575         #            |  |    |  |
00576         #            <--v    <--v
00577         #
00578         # So this is the nightmare:  when generation 0 gets collected, we see
00579         # that c2 has a callback-free weakref, and c1 doesn't even have a
00580         # weakref.  Collecting generation 0 doesn't see c0 at all, and c0 is
00581         # the only object that has a weakref with a callback.  gc clears c1
00582         # and c2.  Clearing c1 has the side effect of dropping the refcount on
00583         # c0 to 0, so c0 goes away (despite that it's in an older generation)
00584         # and c0's wr callback triggers.  That in turn materializes a reference
00585         # to c2 via c2wr(), but c2 gets cleared anyway by gc.
00586 
00587         # We want to let gc happen "naturally", to preserve the distinction
00588         # between generations.
00589         junk = []
00590         i = 0
00591         detector = GC_Detector()
00592         while not detector.gc_happened:
00593             i += 1
00594             if i > 10000:
00595                 self.fail("gc didn't happen after 10000 iterations")
00596             self.assertEqual(len(ouch), 0)
00597             junk.append([])  # this will eventually trigger gc
00598 
00599         self.assertEqual(len(ouch), 1)  # else the callback wasn't invoked
00600         for x in ouch:
00601             # If the callback resurrected c2, the instance would be damaged,
00602             # with an empty __dict__.
00603             self.assertEqual(x, None)

Here is the call graph for this function:

Definition at line 604 of file test_gc.py.

00604 
00605     def test_bug1055820d(self):
00606         # Corresponds to temp2d.py in the bug report.  This is very much like
00607         # test_bug1055820c, but uses a __del__ method instead of a weakref
00608         # callback to sneak in a resurrection of cyclic trash.
00609 
00610         ouch = []
00611         class D(C1055820):
00612             def __del__(self):
00613                 ouch[:] = [c2wr()]
00614 
00615         d0 = D(0)
00616         # Move all the above into generation 2.
00617         gc.collect()
00618 
00619         c1 = C1055820(1)
00620         c1.keep_d0_alive = d0
00621         del d0.loop # now only c1 keeps d0 alive
00622 
00623         c2 = C1055820(2)
00624         c2wr = weakref.ref(c2) # no callback!
00625 
00626         d0 = c1 = c2 = None
00627 
00628         # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
00629         # generation 2.  The only thing keeping it alive is that c1 points to
00630         # it.  c1 and c2 are in generation 0, and are in self-loops.  There's
00631         # a global weakref to c2 (c2wr), but that weakref has no callback.
00632         # There are no other weakrefs.
00633         #
00634         #               d0 has a __del__ method that references c2wr
00635         #               ^
00636         #               |
00637         #               |     Generation 2 above dots
00638         #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
00639         #               |     Generation 0 below dots
00640         #               |
00641         #               |
00642         #            ^->c1   ^->c2 has a wr but no callback
00643         #            |  |    |  |
00644         #            <--v    <--v
00645         #
00646         # So this is the nightmare:  when generation 0 gets collected, we see
00647         # that c2 has a callback-free weakref, and c1 doesn't even have a
00648         # weakref.  Collecting generation 0 doesn't see d0 at all.  gc clears
00649         # c1 and c2.  Clearing c1 has the side effect of dropping the refcount
00650         # on d0 to 0, so d0 goes away (despite that it's in an older
00651         # generation) and d0's __del__ triggers.  That in turn materializes
00652         # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
00653 
00654         # We want to let gc happen "naturally", to preserve the distinction
00655         # between generations.
00656         detector = GC_Detector()
00657         junk = []
00658         i = 0
00659         while not detector.gc_happened:
00660             i += 1
00661             if i > 10000:
00662                 self.fail("gc didn't happen after 10000 iterations")
00663             self.assertEqual(len(ouch), 0)
00664             junk.append([])  # this will eventually trigger gc
00665 
00666         self.assertEqual(len(ouch), 1)  # else __del__ wasn't invoked
00667         for x in ouch:
00668             # If __del__ resurrected c2, the instance would be damaged, with an
00669             # empty __dict__.
00670             self.assertEqual(x, None)

Here is the call graph for this function:


The documentation for this class was generated from the following file: