PageRenderTime 389ms CodeModel.GetById 100ms app.highlight 165ms RepoModel.GetById 117ms app.codeStats 1ms

/Lib/test/test_gc.py

http://unladen-swallow.googlecode.com/
Python | 617 lines | 390 code | 85 blank | 142 comment | 42 complexity | 3b4cc69c294c0f93c1628f7c88e4e953 MD5 | raw file
  1import unittest
  2from test.test_support import verbose, run_unittest
  3import sys
  4import gc
  5import weakref
  6try:
  7    import _llvm
  8except ImportError:
  9    _llvm = None
 10
 11### Support code
 12###############################################################################
 13
 14# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
 15# cyclic gc.
 16
 17# An instance of C1055820 has a self-loop, so becomes cyclic trash when
 18# unreachable.
 19class C1055820(object):
 20    def __init__(self, i):
 21        self.i = i
 22        self.loop = self
 23
 24class GC_Detector(object):
 25    # Create an instance I.  Then gc hasn't happened again so long as
 26    # I.gc_happened is false.
 27
 28    def __init__(self):
 29        self.gc_happened = False
 30
 31        def it_happened(ignored):
 32            self.gc_happened = True
 33
 34        # Create a piece of cyclic trash that triggers it_happened when
 35        # gc collects it.
 36        self.wr = weakref.ref(C1055820(666), it_happened)
 37
 38
 39### Tests
 40###############################################################################
 41
 42class GCTests(unittest.TestCase):
 43    def test_list(self):
 44        l = []
 45        l.append(l)
 46        gc.collect()
 47        del l
 48        self.assertEqual(gc.collect(), 1)
 49
 50    def test_dict(self):
 51        d = {}
 52        d[1] = d
 53        gc.collect()
 54        del d
 55        self.assertEqual(gc.collect(), 1)
 56
 57    def test_tuple(self):
 58        # since tuples are immutable we close the loop with a list
 59        l = []
 60        t = (l,)
 61        l.append(t)
 62        gc.collect()
 63        del t
 64        del l
 65        self.assertEqual(gc.collect(), 2)
 66
 67    def test_class(self):
 68        class A:
 69            pass
 70        A.a = A
 71        gc.collect()
 72        del A
 73        self.assertNotEqual(gc.collect(), 0)
 74
 75    def test_newstyleclass(self):
 76        class A(object):
 77            pass
 78        gc.collect()
 79        del A
 80        self.assertNotEqual(gc.collect(), 0)
 81
 82    def test_instance(self):
 83        class A:
 84            pass
 85        a = A()
 86        a.a = a
 87        gc.collect()
 88        del a
 89        self.assertNotEqual(gc.collect(), 0)
 90
 91    def test_newinstance(self):
 92        class A(object):
 93            pass
 94        a = A()
 95        a.a = a
 96        gc.collect()
 97        del a
 98        self.assertNotEqual(gc.collect(), 0)
 99        class B(list):
100            pass
101        class C(B, A):
102            pass
103        a = C()
104        a.a = a
105        gc.collect()
106        del a
107        self.assertNotEqual(gc.collect(), 0)
108        del B, C
109        # If we don't clear the feedback array, the FDO system will hold
110        # references to B and C, which prevent gc.collect() from collecting
111        # them.
112        if _llvm:
113            _llvm.clear_feedback(self.test_newinstance)
114        self.assertNotEqual(gc.collect(), 0)
115        A.a = A()
116        del A
117        self.assertNotEqual(gc.collect(), 0)
118        self.assertEqual(gc.collect(), 0)
119
120    def test_method(self):
121        # Tricky: self.__init__ is a bound method, it references the instance.
122        class A:
123            def __init__(self):
124                self.init = self.__init__
125        a = A()
126        gc.collect()
127        del a
128        self.assertNotEqual(gc.collect(), 0)
129
130    def test_finalizer(self):
131        # A() is uncollectable if it is part of a cycle, make sure it shows up
132        # in gc.garbage.
133        class A:
134            def __del__(self): pass
135        class B:
136            pass
137        a = A()
138        a.a = a
139        id_a = id(a)
140        b = B()
141        b.b = b
142        gc.collect()
143        del a
144        del b
145        self.assertNotEqual(gc.collect(), 0)
146        for obj in gc.garbage:
147            if id(obj) == id_a:
148                del obj.a
149                break
150        else:
151            self.fail("didn't find obj in garbage (finalizer)")
152        gc.garbage.remove(obj)
153
154    def test_finalizer_newclass(self):
155        # A() is uncollectable if it is part of a cycle, make sure it shows up
156        # in gc.garbage.
157        class A(object):
158            def __del__(self): pass
159        class B(object):
160            pass
161        a = A()
162        a.a = a
163        id_a = id(a)
164        b = B()
165        b.b = b
166        gc.collect()
167        del a
168        del b
169        self.assertNotEqual(gc.collect(), 0)
170        for obj in gc.garbage:
171            if id(obj) == id_a:
172                del obj.a
173                break
174        else:
175            self.fail("didn't find obj in garbage (finalizer)")
176        gc.garbage.remove(obj)
177
178    def test_function(self):
179        # Tricky: f -> d -> f, code should call d.clear() after the exec to
180        # break the cycle.
181        d = {}
182        exec("def f(): pass\n") in d
183        gc.collect()
184        del d
185        self.assertEqual(gc.collect(), 2)
186
187    def test_frame(self):
188        def f():
189            frame = sys._getframe()
190        gc.collect()
191        f()
192        self.assertEqual(gc.collect(), 1)
193
194    def test_saveall(self):
195        # Verify that cyclic garbage like lists show up in gc.garbage if the
196        # SAVEALL option is enabled.
197
198        # First make sure we don't save away other stuff that just happens to
199        # be waiting for collection.
200        gc.collect()
201        # if this fails, someone else created immortal trash
202        self.assertEqual(gc.garbage, [])
203
204        L = []
205        L.append(L)
206        id_L = id(L)
207
208        debug = gc.get_debug()
209        gc.set_debug(debug | gc.DEBUG_SAVEALL)
210        del L
211        gc.collect()
212        gc.set_debug(debug)
213
214        self.assertEqual(len(gc.garbage), 1)
215        obj = gc.garbage.pop()
216        self.assertEqual(id(obj), id_L)
217
218    def test_del(self):
219        # __del__ methods can trigger collection, make this to happen
220        thresholds = gc.get_threshold()
221        gc.enable()
222        gc.set_threshold(1)
223
224        class A:
225            def __del__(self):
226                dir(self)
227        a = A()
228        del a
229
230        gc.disable()
231        gc.set_threshold(*thresholds)
232
233    def test_del_newclass(self):
234        # __del__ methods can trigger collection, make this to happen
235        thresholds = gc.get_threshold()
236        gc.enable()
237        gc.set_threshold(1)
238
239        class A(object):
240            def __del__(self):
241                dir(self)
242        a = A()
243        del a
244
245        gc.disable()
246        gc.set_threshold(*thresholds)
247
248    # The following two tests are fragile:
249    # They precisely count the number of allocations,
250    # which is highly implementation-dependent.
251    # For example:
252    # - disposed tuples are not freed, but reused
253    # - the call to assertEqual somehow avoids building its args tuple
254    def test_get_count(self):
255        # Avoid future allocation of method object
256        assertEqual = self.assertEqual
257        gc.collect()
258        assertEqual(gc.get_count(), (0, 0, 0))
259        a = dict()
260        # since gc.collect(), we created two objects:
261        # the dict, and the tuple returned by get_count()
262        assertEqual(gc.get_count(), (2, 0, 0))
263
264    def test_collect_generations(self):
265        # Avoid future allocation of method object
266        assertEqual = self.assertEqual
267        gc.collect()
268        a = dict()
269        gc.collect(0)
270        assertEqual(gc.get_count(), (0, 1, 0))
271        gc.collect(1)
272        assertEqual(gc.get_count(), (0, 0, 1))
273        gc.collect(2)
274        assertEqual(gc.get_count(), (0, 0, 0))
275
276    def test_trashcan(self):
277        class Ouch:
278            n = 0
279            def __del__(self):
280                Ouch.n = Ouch.n + 1
281                if Ouch.n % 17 == 0:
282                    gc.collect()
283
284        # "trashcan" is a hack to prevent stack overflow when deallocating
285        # very deeply nested tuples etc.  It works in part by abusing the
286        # type pointer and refcount fields, and that can yield horrible
287        # problems when gc tries to traverse the structures.
288        # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
289        # most likely die via segfault.
290
291        # Note:  In 2.3 the possibility for compiling without cyclic gc was
292        # removed, and that in turn allows the trashcan mechanism to work
293        # via much simpler means (e.g., it never abuses the type pointer or
294        # refcount fields anymore).  Since it's much less likely to cause a
295        # problem now, the various constants in this expensive (we force a lot
296        # of full collections) test are cut back from the 2.2 version.
297        gc.enable()
298        N = 150
299        for count in range(2):
300            t = []
301            for i in range(N):
302                t = [t, Ouch()]
303            u = []
304            for i in range(N):
305                u = [u, Ouch()]
306            v = {}
307            for i in range(N):
308                v = {1: v, 2: Ouch()}
309        gc.disable()
310
311    def test_boom(self):
312        class Boom:
313            def __getattr__(self, someattribute):
314                del self.attr
315                raise AttributeError
316
317        a = Boom()
318        b = Boom()
319        a.attr = b
320        b.attr = a
321
322        gc.collect()
323        garbagelen = len(gc.garbage)
324        del a, b
325        # a<->b are in a trash cycle now.  Collection will invoke
326        # Boom.__getattr__ (to see whether a and b have __del__ methods), and
327        # __getattr__ deletes the internal "attr" attributes as a side effect.
328        # That causes the trash cycle to get reclaimed via refcounts falling to
329        # 0, thus mutating the trash graph as a side effect of merely asking
330        # whether __del__ exists.  This used to (before 2.3b1) crash Python.
331        # Now __getattr__ isn't called.
332        self.assertEqual(gc.collect(), 4)
333        self.assertEqual(len(gc.garbage), garbagelen)
334
335    def test_boom2(self):
336        class Boom2:
337            def __init__(self):
338                self.x = 0
339
340            def __getattr__(self, someattribute):
341                self.x += 1
342                if self.x > 1:
343                    del self.attr
344                raise AttributeError
345
346        a = Boom2()
347        b = Boom2()
348        a.attr = b
349        b.attr = a
350
351        gc.collect()
352        garbagelen = len(gc.garbage)
353        del a, b
354        # Much like test_boom(), except that __getattr__ doesn't break the
355        # cycle until the second time gc checks for __del__.  As of 2.3b1,
356        # there isn't a second time, so this simply cleans up the trash cycle.
357        # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
358        # reclaimed this way.
359        self.assertEqual(gc.collect(), 4)
360        self.assertEqual(len(gc.garbage), garbagelen)
361
362    def test_boom_new(self):
363        # boom__new and boom2_new are exactly like boom and boom2, except use
364        # new-style classes.
365
366        class Boom_New(object):
367            def __getattr__(self, someattribute):
368                del self.attr
369                raise AttributeError
370
371        a = Boom_New()
372        b = Boom_New()
373        a.attr = b
374        b.attr = a
375
376        gc.collect()
377        garbagelen = len(gc.garbage)
378        del a, b
379        self.assertEqual(gc.collect(), 4)
380        self.assertEqual(len(gc.garbage), garbagelen)
381
382    def test_boom2_new(self):
383        class Boom2_New(object):
384            def __init__(self):
385                self.x = 0
386
387            def __getattr__(self, someattribute):
388                self.x += 1
389                if self.x > 1:
390                    del self.attr
391                raise AttributeError
392
393        a = Boom2_New()
394        b = Boom2_New()
395        a.attr = b
396        b.attr = a
397
398        gc.collect()
399        garbagelen = len(gc.garbage)
400        del a, b
401        self.assertEqual(gc.collect(), 4)
402        self.assertEqual(len(gc.garbage), garbagelen)
403
404    def test_get_referents(self):
405        alist = [1, 3, 5]
406        got = gc.get_referents(alist)
407        got.sort()
408        self.assertEqual(got, alist)
409
410        atuple = tuple(alist)
411        got = gc.get_referents(atuple)
412        got.sort()
413        self.assertEqual(got, alist)
414
415        adict = {1: 3, 5: 7}
416        expected = [1, 3, 5, 7]
417        got = gc.get_referents(adict)
418        got.sort()
419        self.assertEqual(got, expected)
420
421        got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
422        got.sort()
423        self.assertEqual(got, [0, 0] + range(5))
424
425        self.assertEqual(gc.get_referents(1, 'a', 4j), [])
426
427    def test_bug1055820b(self):
428        # Corresponds to temp2b.py in the bug report.
429
430        ouch = []
431        def callback(ignored):
432            ouch[:] = [wr() for wr in WRs]
433
434        Cs = [C1055820(i) for i in range(2)]
435        WRs = [weakref.ref(c, callback) for c in Cs]
436        c = None
437
438        gc.collect()
439        self.assertEqual(len(ouch), 0)
440        # Make the two instances trash, and collect again.  The bug was that
441        # the callback materialized a strong reference to an instance, but gc
442        # cleared the instance's dict anyway.
443        Cs = None
444        gc.collect()
445        self.assertEqual(len(ouch), 2)  # else the callbacks didn't run
446        for x in ouch:
447            # If the callback resurrected one of these guys, the instance
448            # would be damaged, with an empty __dict__.
449            self.assertEqual(x, None)
450
451class GCTogglingTests(unittest.TestCase):
452    def setUp(self):
453        gc.enable()
454
455    def tearDown(self):
456        gc.disable()
457
458    def test_bug1055820c(self):
459        # Corresponds to temp2c.py in the bug report.  This is pretty
460        # elaborate.
461
462        c0 = C1055820(0)
463        # Move c0 into generation 2.
464        gc.collect()
465
466        c1 = C1055820(1)
467        c1.keep_c0_alive = c0
468        del c0.loop # now only c1 keeps c0 alive
469
470        c2 = C1055820(2)
471        c2wr = weakref.ref(c2) # no callback!
472
473        ouch = []
474        def callback(ignored):
475            ouch[:] = [c2wr()]
476
477        # The callback gets associated with a wr on an object in generation 2.
478        c0wr = weakref.ref(c0, callback)
479
480        c0 = c1 = c2 = None
481
482        # What we've set up:  c0, c1, and c2 are all trash now.  c0 is in
483        # generation 2.  The only thing keeping it alive is that c1 points to
484        # it. c1 and c2 are in generation 0, and are in self-loops.  There's a
485        # global weakref to c2 (c2wr), but that weakref has no callback.
486        # There's also a global weakref to c0 (c0wr), and that does have a
487        # callback, and that callback references c2 via c2wr().
488        #
489        #               c0 has a wr with callback, which references c2wr
490        #               ^
491        #               |
492        #               |     Generation 2 above dots
493        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
494        #               |     Generation 0 below dots
495        #               |
496        #               |
497        #            ^->c1   ^->c2 has a wr but no callback
498        #            |  |    |  |
499        #            <--v    <--v
500        #
501        # So this is the nightmare:  when generation 0 gets collected, we see
502        # that c2 has a callback-free weakref, and c1 doesn't even have a
503        # weakref.  Collecting generation 0 doesn't see c0 at all, and c0 is
504        # the only object that has a weakref with a callback.  gc clears c1
505        # and c2.  Clearing c1 has the side effect of dropping the refcount on
506        # c0 to 0, so c0 goes away (despite that it's in an older generation)
507        # and c0's wr callback triggers.  That in turn materializes a reference
508        # to c2 via c2wr(), but c2 gets cleared anyway by gc.
509
510        # We want to let gc happen "naturally", to preserve the distinction
511        # between generations.
512        junk = []
513        i = 0
514        detector = GC_Detector()
515        while not detector.gc_happened:
516            i += 1
517            if i > 10000:
518                self.fail("gc didn't happen after 10000 iterations")
519            self.assertEqual(len(ouch), 0)
520            junk.append([])  # this will eventually trigger gc
521
522        self.assertEqual(len(ouch), 1)  # else the callback wasn't invoked
523        for x in ouch:
524            # If the callback resurrected c2, the instance would be damaged,
525            # with an empty __dict__.
526            self.assertEqual(x, None)
527
528    def test_bug1055820d(self):
529        # Corresponds to temp2d.py in the bug report.  This is very much like
530        # test_bug1055820c, but uses a __del__ method instead of a weakref
531        # callback to sneak in a resurrection of cyclic trash.
532
533        ouch = []
534        class D(C1055820):
535            def __del__(self):
536                ouch[:] = [c2wr()]
537
538        d0 = D(0)
539        # Move all the above into generation 2.
540        gc.collect()
541
542        c1 = C1055820(1)
543        c1.keep_d0_alive = d0
544        del d0.loop # now only c1 keeps d0 alive
545
546        c2 = C1055820(2)
547        c2wr = weakref.ref(c2) # no callback!
548
549        d0 = c1 = c2 = None
550
551        # What we've set up:  d0, c1, and c2 are all trash now.  d0 is in
552        # generation 2.  The only thing keeping it alive is that c1 points to
553        # it.  c1 and c2 are in generation 0, and are in self-loops.  There's
554        # a global weakref to c2 (c2wr), but that weakref has no callback.
555        # There are no other weakrefs.
556        #
557        #               d0 has a __del__ method that references c2wr
558        #               ^
559        #               |
560        #               |     Generation 2 above dots
561        #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
562        #               |     Generation 0 below dots
563        #               |
564        #               |
565        #            ^->c1   ^->c2 has a wr but no callback
566        #            |  |    |  |
567        #            <--v    <--v
568        #
569        # So this is the nightmare:  when generation 0 gets collected, we see
570        # that c2 has a callback-free weakref, and c1 doesn't even have a
571        # weakref.  Collecting generation 0 doesn't see d0 at all.  gc clears
572        # c1 and c2.  Clearing c1 has the side effect of dropping the refcount
573        # on d0 to 0, so d0 goes away (despite that it's in an older
574        # generation) and d0's __del__ triggers.  That in turn materializes
575        # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
576
577        # We want to let gc happen "naturally", to preserve the distinction
578        # between generations.
579        detector = GC_Detector()
580        junk = []
581        i = 0
582        while not detector.gc_happened:
583            i += 1
584            if i > 10000:
585                self.fail("gc didn't happen after 10000 iterations")
586            self.assertEqual(len(ouch), 0)
587            junk.append([])  # this will eventually trigger gc
588
589        self.assertEqual(len(ouch), 1)  # else __del__ wasn't invoked
590        for x in ouch:
591            # If __del__ resurrected c2, the instance would be damaged, with an
592            # empty __dict__.
593            self.assertEqual(x, None)
594
595def test_main():
596    enabled = gc.isenabled()
597    gc.disable()
598    assert not gc.isenabled()
599    debug = gc.get_debug()
600    gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
601
602    try:
603        gc.collect() # Delete 2nd generation garbage
604        run_unittest(GCTests, GCTogglingTests)
605    finally:
606        gc.set_debug(debug)
607        # test gc.enable() even if GC is disabled by default
608        if verbose:
609            print "restoring automatic collection"
610        # make sure to always test gc.enable()
611        gc.enable()
612        assert gc.isenabled()
613        if not enabled:
614            gc.disable()
615
616if __name__ == "__main__":
617    test_main()