blob: e1c124d160fb7945da9994a5c39a0dcd385a6722 [file] [log] [blame]
Guido van Rossumd8faa362007-04-27 19:54:29 +00001import unittest
Antoine Pitrou696e0352010-08-08 22:18:46 +00002from test.support import verbose, run_unittest, strip_python_stderr
Neil Schemenauer88c761a2001-07-12 13:25:53 +00003import sys
Antoine Pitrou56cd62c2012-09-06 00:59:49 +02004import time
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00005import gc
Tim Petersead8b7a2004-10-30 23:09:22 +00006import weakref
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00007
Antoine Pitrou56cd62c2012-09-06 00:59:49 +02008try:
9 import threading
10except ImportError:
11 threading = None
12
Guido van Rossumd8faa362007-04-27 19:54:29 +000013### Support code
14###############################################################################
Tim Peters0f81ab62003-04-08 16:39:48 +000015
Tim Petersead8b7a2004-10-30 23:09:22 +000016# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
17# cyclic gc.
18
19# An instance of C1055820 has a self-loop, so becomes cyclic trash when
20# unreachable.
21class C1055820(object):
22 def __init__(self, i):
23 self.i = i
24 self.loop = self
25
26class GC_Detector(object):
27 # Create an instance I. Then gc hasn't happened again so long as
28 # I.gc_happened is false.
29
30 def __init__(self):
31 self.gc_happened = False
32
33 def it_happened(ignored):
34 self.gc_happened = True
35
36 # Create a piece of cyclic trash that triggers it_happened when
37 # gc collects it.
38 self.wr = weakref.ref(C1055820(666), it_happened)
39
Tim Petersead8b7a2004-10-30 23:09:22 +000040
Guido van Rossumd8faa362007-04-27 19:54:29 +000041### Tests
42###############################################################################
Tim Petersead8b7a2004-10-30 23:09:22 +000043
Guido van Rossumd8faa362007-04-27 19:54:29 +000044class GCTests(unittest.TestCase):
45 def test_list(self):
46 l = []
47 l.append(l)
48 gc.collect()
49 del l
50 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000051
Guido van Rossumd8faa362007-04-27 19:54:29 +000052 def test_dict(self):
53 d = {}
54 d[1] = d
55 gc.collect()
56 del d
57 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000058
Guido van Rossumd8faa362007-04-27 19:54:29 +000059 def test_tuple(self):
60 # since tuples are immutable we close the loop with a list
61 l = []
62 t = (l,)
63 l.append(t)
64 gc.collect()
65 del t
66 del l
67 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +000068
Guido van Rossumd8faa362007-04-27 19:54:29 +000069 def test_class(self):
70 class A:
71 pass
72 A.a = A
73 gc.collect()
74 del A
75 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000076
Guido van Rossumd8faa362007-04-27 19:54:29 +000077 def test_newstyleclass(self):
78 class A(object):
79 pass
80 gc.collect()
81 del A
82 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000083
Guido van Rossumd8faa362007-04-27 19:54:29 +000084 def test_instance(self):
85 class A:
86 pass
87 a = A()
88 a.a = a
89 gc.collect()
90 del a
91 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000092
Guido van Rossumd8faa362007-04-27 19:54:29 +000093 def test_newinstance(self):
94 class A(object):
95 pass
96 a = A()
97 a.a = a
98 gc.collect()
99 del a
100 self.assertNotEqual(gc.collect(), 0)
101 class B(list):
102 pass
103 class C(B, A):
104 pass
105 a = C()
106 a.a = a
107 gc.collect()
108 del a
109 self.assertNotEqual(gc.collect(), 0)
110 del B, C
111 self.assertNotEqual(gc.collect(), 0)
112 A.a = A()
113 del A
114 self.assertNotEqual(gc.collect(), 0)
115 self.assertEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000116
Guido van Rossumd8faa362007-04-27 19:54:29 +0000117 def test_method(self):
118 # Tricky: self.__init__ is a bound method, it references the instance.
119 class A:
120 def __init__(self):
121 self.init = self.__init__
122 a = A()
123 gc.collect()
124 del a
125 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000126
Guido van Rossumd8faa362007-04-27 19:54:29 +0000127 def test_finalizer(self):
128 # A() is uncollectable if it is part of a cycle, make sure it shows up
129 # in gc.garbage.
130 class A:
131 def __del__(self): pass
132 class B:
133 pass
134 a = A()
135 a.a = a
136 id_a = id(a)
137 b = B()
138 b.b = b
139 gc.collect()
140 del a
141 del b
142 self.assertNotEqual(gc.collect(), 0)
143 for obj in gc.garbage:
144 if id(obj) == id_a:
145 del obj.a
146 break
147 else:
148 self.fail("didn't find obj in garbage (finalizer)")
149 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000150
Guido van Rossumd8faa362007-04-27 19:54:29 +0000151 def test_finalizer_newclass(self):
152 # A() is uncollectable if it is part of a cycle, make sure it shows up
153 # in gc.garbage.
154 class A(object):
155 def __del__(self): pass
156 class B(object):
157 pass
158 a = A()
159 a.a = a
160 id_a = id(a)
161 b = B()
162 b.b = b
163 gc.collect()
164 del a
165 del b
166 self.assertNotEqual(gc.collect(), 0)
167 for obj in gc.garbage:
168 if id(obj) == id_a:
169 del obj.a
170 break
171 else:
172 self.fail("didn't find obj in garbage (finalizer)")
173 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000174
Guido van Rossumd8faa362007-04-27 19:54:29 +0000175 def test_function(self):
176 # Tricky: f -> d -> f, code should call d.clear() after the exec to
177 # break the cycle.
178 d = {}
179 exec("def f(): pass\n", d)
180 gc.collect()
181 del d
182 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +0000183
Guido van Rossumd8faa362007-04-27 19:54:29 +0000184 def test_frame(self):
185 def f():
186 frame = sys._getframe()
187 gc.collect()
188 f()
189 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +0000190
Guido van Rossumd8faa362007-04-27 19:54:29 +0000191 def test_saveall(self):
192 # Verify that cyclic garbage like lists show up in gc.garbage if the
193 # SAVEALL option is enabled.
Tim Petersead8b7a2004-10-30 23:09:22 +0000194
Guido van Rossumd8faa362007-04-27 19:54:29 +0000195 # First make sure we don't save away other stuff that just happens to
196 # be waiting for collection.
197 gc.collect()
198 # if this fails, someone else created immortal trash
199 self.assertEqual(gc.garbage, [])
200
201 L = []
202 L.append(L)
203 id_L = id(L)
204
205 debug = gc.get_debug()
206 gc.set_debug(debug | gc.DEBUG_SAVEALL)
207 del L
208 gc.collect()
209 gc.set_debug(debug)
210
211 self.assertEqual(len(gc.garbage), 1)
212 obj = gc.garbage.pop()
213 self.assertEqual(id(obj), id_L)
214
215 def test_del(self):
216 # __del__ methods can trigger collection, make this to happen
217 thresholds = gc.get_threshold()
218 gc.enable()
219 gc.set_threshold(1)
220
221 class A:
222 def __del__(self):
223 dir(self)
224 a = A()
225 del a
226
227 gc.disable()
228 gc.set_threshold(*thresholds)
229
230 def test_del_newclass(self):
231 # __del__ methods can trigger collection, make this to happen
232 thresholds = gc.get_threshold()
233 gc.enable()
234 gc.set_threshold(1)
235
236 class A(object):
237 def __del__(self):
238 dir(self)
239 a = A()
240 del a
241
242 gc.disable()
243 gc.set_threshold(*thresholds)
244
Christian Heimesa156e092008-02-16 07:38:31 +0000245 # The following two tests are fragile:
246 # They precisely count the number of allocations,
247 # which is highly implementation-dependent.
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200248 # For example, disposed tuples are not freed, but reused.
249 # To minimize variations, though, we first store the get_count() results
250 # and check them at the end.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000251 def test_get_count(self):
252 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200253 a, b, c = gc.get_count()
254 x = []
255 d, e, f = gc.get_count()
256 self.assertEqual((b, c), (0, 0))
257 self.assertEqual((e, f), (0, 0))
258 # This is less fragile than asserting that a equals 0.
259 self.assertLess(a, 5)
260 # Between the two calls to get_count(), at least one object was
261 # created (the list).
262 self.assertGreater(d, a)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000263
264 def test_collect_generations(self):
265 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200266 # This object will "trickle" into generation N + 1 after
267 # each call to collect(N)
268 x = []
Guido van Rossumd8faa362007-04-27 19:54:29 +0000269 gc.collect(0)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200270 # x is now in gen 1
271 a, b, c = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000272 gc.collect(1)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200273 # x is now in gen 2
274 d, e, f = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000275 gc.collect(2)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200276 # x is now in gen 3
277 g, h, i = gc.get_count()
278 # We don't check a, d, g since their exact values depends on
279 # internal implementation details of the interpreter.
280 self.assertEqual((b, c), (1, 0))
281 self.assertEqual((e, f), (0, 1))
282 self.assertEqual((h, i), (0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000283
284 def test_trashcan(self):
285 class Ouch:
286 n = 0
287 def __del__(self):
288 Ouch.n = Ouch.n + 1
289 if Ouch.n % 17 == 0:
290 gc.collect()
291
292 # "trashcan" is a hack to prevent stack overflow when deallocating
293 # very deeply nested tuples etc. It works in part by abusing the
294 # type pointer and refcount fields, and that can yield horrible
295 # problems when gc tries to traverse the structures.
296 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
297 # most likely die via segfault.
298
299 # Note: In 2.3 the possibility for compiling without cyclic gc was
300 # removed, and that in turn allows the trashcan mechanism to work
301 # via much simpler means (e.g., it never abuses the type pointer or
302 # refcount fields anymore). Since it's much less likely to cause a
303 # problem now, the various constants in this expensive (we force a lot
304 # of full collections) test are cut back from the 2.2 version.
305 gc.enable()
306 N = 150
307 for count in range(2):
308 t = []
309 for i in range(N):
310 t = [t, Ouch()]
311 u = []
312 for i in range(N):
313 u = [u, Ouch()]
314 v = {}
315 for i in range(N):
316 v = {1: v, 2: Ouch()}
317 gc.disable()
318
Antoine Pitrou56cd62c2012-09-06 00:59:49 +0200319 @unittest.skipUnless(threading, "test meaningless on builds without threads")
320 def test_trashcan_threads(self):
321 # Issue #13992: trashcan mechanism should be thread-safe
322 NESTING = 60
323 N_THREADS = 2
324
325 def sleeper_gen():
326 """A generator that releases the GIL when closed or dealloc'ed."""
327 try:
328 yield
329 finally:
330 time.sleep(0.000001)
331
332 class C(list):
333 # Appending to a list is atomic, which avoids the use of a lock.
334 inits = []
335 dels = []
336 def __init__(self, alist):
337 self[:] = alist
338 C.inits.append(None)
339 def __del__(self):
340 # This __del__ is called by subtype_dealloc().
341 C.dels.append(None)
342 # `g` will release the GIL when garbage-collected. This
343 # helps assert subtype_dealloc's behaviour when threads
344 # switch in the middle of it.
345 g = sleeper_gen()
346 next(g)
347 # Now that __del__ is finished, subtype_dealloc will proceed
348 # to call list_dealloc, which also uses the trashcan mechanism.
349
350 def make_nested():
351 """Create a sufficiently nested container object so that the
352 trashcan mechanism is invoked when deallocating it."""
353 x = C([])
354 for i in range(NESTING):
355 x = [C([x])]
356 del x
357
358 def run_thread():
359 """Exercise make_nested() in a loop."""
360 while not exit:
361 make_nested()
362
363 old_switchinterval = sys.getswitchinterval()
364 sys.setswitchinterval(1e-5)
365 try:
366 exit = False
367 threads = []
368 for i in range(N_THREADS):
369 t = threading.Thread(target=run_thread)
370 threads.append(t)
371 for t in threads:
372 t.start()
373 time.sleep(1.0)
374 exit = True
375 for t in threads:
376 t.join()
377 finally:
378 sys.setswitchinterval(old_switchinterval)
379 gc.collect()
380 self.assertEqual(len(C.inits), len(C.dels))
381
Guido van Rossumd8faa362007-04-27 19:54:29 +0000382 def test_boom(self):
383 class Boom:
384 def __getattr__(self, someattribute):
385 del self.attr
386 raise AttributeError
387
388 a = Boom()
389 b = Boom()
390 a.attr = b
391 b.attr = a
392
393 gc.collect()
394 garbagelen = len(gc.garbage)
395 del a, b
396 # a<->b are in a trash cycle now. Collection will invoke
397 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
398 # __getattr__ deletes the internal "attr" attributes as a side effect.
399 # That causes the trash cycle to get reclaimed via refcounts falling to
400 # 0, thus mutating the trash graph as a side effect of merely asking
401 # whether __del__ exists. This used to (before 2.3b1) crash Python.
402 # Now __getattr__ isn't called.
403 self.assertEqual(gc.collect(), 4)
404 self.assertEqual(len(gc.garbage), garbagelen)
405
406 def test_boom2(self):
407 class Boom2:
408 def __init__(self):
409 self.x = 0
410
411 def __getattr__(self, someattribute):
412 self.x += 1
413 if self.x > 1:
414 del self.attr
415 raise AttributeError
416
417 a = Boom2()
418 b = Boom2()
419 a.attr = b
420 b.attr = a
421
422 gc.collect()
423 garbagelen = len(gc.garbage)
424 del a, b
425 # Much like test_boom(), except that __getattr__ doesn't break the
426 # cycle until the second time gc checks for __del__. As of 2.3b1,
427 # there isn't a second time, so this simply cleans up the trash cycle.
428 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
429 # reclaimed this way.
430 self.assertEqual(gc.collect(), 4)
431 self.assertEqual(len(gc.garbage), garbagelen)
432
433 def test_boom_new(self):
434 # boom__new and boom2_new are exactly like boom and boom2, except use
435 # new-style classes.
436
437 class Boom_New(object):
438 def __getattr__(self, someattribute):
439 del self.attr
440 raise AttributeError
441
442 a = Boom_New()
443 b = Boom_New()
444 a.attr = b
445 b.attr = a
446
447 gc.collect()
448 garbagelen = len(gc.garbage)
449 del a, b
450 self.assertEqual(gc.collect(), 4)
451 self.assertEqual(len(gc.garbage), garbagelen)
452
453 def test_boom2_new(self):
454 class Boom2_New(object):
455 def __init__(self):
456 self.x = 0
457
458 def __getattr__(self, someattribute):
459 self.x += 1
460 if self.x > 1:
461 del self.attr
462 raise AttributeError
463
464 a = Boom2_New()
465 b = Boom2_New()
466 a.attr = b
467 b.attr = a
468
469 gc.collect()
470 garbagelen = len(gc.garbage)
471 del a, b
472 self.assertEqual(gc.collect(), 4)
473 self.assertEqual(len(gc.garbage), garbagelen)
474
475 def test_get_referents(self):
476 alist = [1, 3, 5]
477 got = gc.get_referents(alist)
478 got.sort()
479 self.assertEqual(got, alist)
480
481 atuple = tuple(alist)
482 got = gc.get_referents(atuple)
483 got.sort()
484 self.assertEqual(got, alist)
485
486 adict = {1: 3, 5: 7}
487 expected = [1, 3, 5, 7]
488 got = gc.get_referents(adict)
489 got.sort()
490 self.assertEqual(got, expected)
491
492 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
493 got.sort()
Guido van Rossum805365e2007-05-07 22:24:25 +0000494 self.assertEqual(got, [0, 0] + list(range(5)))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000495
496 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
497
Antoine Pitrou3a652b12009-03-23 18:52:06 +0000498 def test_is_tracked(self):
499 # Atomic built-in types are not tracked, user-defined objects and
500 # mutable containers are.
501 # NOTE: types with special optimizations (e.g. tuple) have tests
502 # in their own test files instead.
503 self.assertFalse(gc.is_tracked(None))
504 self.assertFalse(gc.is_tracked(1))
505 self.assertFalse(gc.is_tracked(1.0))
506 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
507 self.assertFalse(gc.is_tracked(True))
508 self.assertFalse(gc.is_tracked(False))
509 self.assertFalse(gc.is_tracked(b"a"))
510 self.assertFalse(gc.is_tracked("a"))
511 self.assertFalse(gc.is_tracked(bytearray(b"a")))
512 self.assertFalse(gc.is_tracked(type))
513 self.assertFalse(gc.is_tracked(int))
514 self.assertFalse(gc.is_tracked(object))
515 self.assertFalse(gc.is_tracked(object()))
516
517 class UserClass:
518 pass
519 self.assertTrue(gc.is_tracked(gc))
520 self.assertTrue(gc.is_tracked(UserClass))
521 self.assertTrue(gc.is_tracked(UserClass()))
522 self.assertTrue(gc.is_tracked([]))
523 self.assertTrue(gc.is_tracked(set()))
524
Guido van Rossumd8faa362007-04-27 19:54:29 +0000525 def test_bug1055820b(self):
526 # Corresponds to temp2b.py in the bug report.
527
528 ouch = []
529 def callback(ignored):
530 ouch[:] = [wr() for wr in WRs]
531
532 Cs = [C1055820(i) for i in range(2)]
533 WRs = [weakref.ref(c, callback) for c in Cs]
534 c = None
535
536 gc.collect()
537 self.assertEqual(len(ouch), 0)
538 # Make the two instances trash, and collect again. The bug was that
539 # the callback materialized a strong reference to an instance, but gc
540 # cleared the instance's dict anyway.
541 Cs = None
542 gc.collect()
543 self.assertEqual(len(ouch), 2) # else the callbacks didn't run
544 for x in ouch:
545 # If the callback resurrected one of these guys, the instance
546 # would be damaged, with an empty __dict__.
547 self.assertEqual(x, None)
548
Antoine Pitrou696e0352010-08-08 22:18:46 +0000549 def test_garbage_at_shutdown(self):
550 import subprocess
551 code = """if 1:
552 import gc
553 class X:
554 def __init__(self, name):
555 self.name = name
556 def __repr__(self):
557 return "<X %%r>" %% self.name
558 def __del__(self):
559 pass
560
561 x = X('first')
562 x.x = x
563 x.y = X('second')
564 del x
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000565 gc.set_debug(%s)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000566 """
567 def run_command(code):
Georg Brandl08be72d2010-10-24 15:11:22 +0000568 p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
Antoine Pitrou696e0352010-08-08 22:18:46 +0000569 stdout=subprocess.PIPE,
570 stderr=subprocess.PIPE)
571 stdout, stderr = p.communicate()
Brian Curtin8291af22010-11-01 16:40:17 +0000572 p.stdout.close()
573 p.stderr.close()
Antoine Pitrou696e0352010-08-08 22:18:46 +0000574 self.assertEqual(p.returncode, 0)
575 self.assertEqual(stdout.strip(), b"")
576 return strip_python_stderr(stderr)
577
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000578 stderr = run_command(code % "0")
Georg Brandl08be72d2010-10-24 15:11:22 +0000579 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
580 b"shutdown; use", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000581 self.assertNotIn(b"<X 'first'>", stderr)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000582 # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000583 stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
Georg Brandl08be72d2010-10-24 15:11:22 +0000584 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
585 b"shutdown", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000586 self.assertTrue(
587 (b"[<X 'first'>, <X 'second'>]" in stderr) or
588 (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000589 # With DEBUG_SAVEALL, no additional message should get printed
590 # (because gc.garbage also contains normally reclaimable cyclic
591 # references, and its elements get printed at runtime anyway).
592 stderr = run_command(code % "gc.DEBUG_SAVEALL")
593 self.assertNotIn(b"uncollectable objects at shutdown", stderr)
594
Antoine Pitrou696e0352010-08-08 22:18:46 +0000595
Guido van Rossumd8faa362007-04-27 19:54:29 +0000596class GCTogglingTests(unittest.TestCase):
597 def setUp(self):
598 gc.enable()
599
600 def tearDown(self):
601 gc.disable()
602
603 def test_bug1055820c(self):
604 # Corresponds to temp2c.py in the bug report. This is pretty
605 # elaborate.
606
607 c0 = C1055820(0)
608 # Move c0 into generation 2.
609 gc.collect()
610
611 c1 = C1055820(1)
612 c1.keep_c0_alive = c0
613 del c0.loop # now only c1 keeps c0 alive
614
615 c2 = C1055820(2)
616 c2wr = weakref.ref(c2) # no callback!
617
618 ouch = []
619 def callback(ignored):
Tim Petersead8b7a2004-10-30 23:09:22 +0000620 ouch[:] = [c2wr()]
621
Guido van Rossumd8faa362007-04-27 19:54:29 +0000622 # The callback gets associated with a wr on an object in generation 2.
623 c0wr = weakref.ref(c0, callback)
Tim Petersead8b7a2004-10-30 23:09:22 +0000624
Guido van Rossumd8faa362007-04-27 19:54:29 +0000625 c0 = c1 = c2 = None
Tim Petersead8b7a2004-10-30 23:09:22 +0000626
Guido van Rossumd8faa362007-04-27 19:54:29 +0000627 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
628 # generation 2. The only thing keeping it alive is that c1 points to
629 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
630 # global weakref to c2 (c2wr), but that weakref has no callback.
631 # There's also a global weakref to c0 (c0wr), and that does have a
632 # callback, and that callback references c2 via c2wr().
633 #
634 # c0 has a wr with callback, which references c2wr
635 # ^
636 # |
637 # | Generation 2 above dots
638 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
639 # | Generation 0 below dots
640 # |
641 # |
642 # ^->c1 ^->c2 has a wr but no callback
643 # | | | |
644 # <--v <--v
645 #
646 # So this is the nightmare: when generation 0 gets collected, we see
647 # that c2 has a callback-free weakref, and c1 doesn't even have a
648 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
649 # the only object that has a weakref with a callback. gc clears c1
650 # and c2. Clearing c1 has the side effect of dropping the refcount on
651 # c0 to 0, so c0 goes away (despite that it's in an older generation)
652 # and c0's wr callback triggers. That in turn materializes a reference
653 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
Tim Petersead8b7a2004-10-30 23:09:22 +0000654
Guido van Rossumd8faa362007-04-27 19:54:29 +0000655 # We want to let gc happen "naturally", to preserve the distinction
656 # between generations.
657 junk = []
658 i = 0
659 detector = GC_Detector()
660 while not detector.gc_happened:
661 i += 1
662 if i > 10000:
663 self.fail("gc didn't happen after 10000 iterations")
664 self.assertEqual(len(ouch), 0)
665 junk.append([]) # this will eventually trigger gc
Tim Petersead8b7a2004-10-30 23:09:22 +0000666
Guido van Rossumd8faa362007-04-27 19:54:29 +0000667 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
668 for x in ouch:
669 # If the callback resurrected c2, the instance would be damaged,
670 # with an empty __dict__.
671 self.assertEqual(x, None)
Tim Petersead8b7a2004-10-30 23:09:22 +0000672
Guido van Rossumd8faa362007-04-27 19:54:29 +0000673 def test_bug1055820d(self):
674 # Corresponds to temp2d.py in the bug report. This is very much like
675 # test_bug1055820c, but uses a __del__ method instead of a weakref
676 # callback to sneak in a resurrection of cyclic trash.
Tim Petersead8b7a2004-10-30 23:09:22 +0000677
Guido van Rossumd8faa362007-04-27 19:54:29 +0000678 ouch = []
679 class D(C1055820):
680 def __del__(self):
681 ouch[:] = [c2wr()]
Tim Petersead8b7a2004-10-30 23:09:22 +0000682
Guido van Rossumd8faa362007-04-27 19:54:29 +0000683 d0 = D(0)
684 # Move all the above into generation 2.
685 gc.collect()
Tim Petersead8b7a2004-10-30 23:09:22 +0000686
Guido van Rossumd8faa362007-04-27 19:54:29 +0000687 c1 = C1055820(1)
688 c1.keep_d0_alive = d0
689 del d0.loop # now only c1 keeps d0 alive
Tim Petersead8b7a2004-10-30 23:09:22 +0000690
Guido van Rossumd8faa362007-04-27 19:54:29 +0000691 c2 = C1055820(2)
692 c2wr = weakref.ref(c2) # no callback!
Tim Petersead8b7a2004-10-30 23:09:22 +0000693
Guido van Rossumd8faa362007-04-27 19:54:29 +0000694 d0 = c1 = c2 = None
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000695
Guido van Rossumd8faa362007-04-27 19:54:29 +0000696 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
697 # generation 2. The only thing keeping it alive is that c1 points to
698 # it. c1 and c2 are in generation 0, and are in self-loops. There's
699 # a global weakref to c2 (c2wr), but that weakref has no callback.
700 # There are no other weakrefs.
701 #
702 # d0 has a __del__ method that references c2wr
703 # ^
704 # |
705 # | Generation 2 above dots
706 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
707 # | Generation 0 below dots
708 # |
709 # |
710 # ^->c1 ^->c2 has a wr but no callback
711 # | | | |
712 # <--v <--v
713 #
714 # So this is the nightmare: when generation 0 gets collected, we see
715 # that c2 has a callback-free weakref, and c1 doesn't even have a
716 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
717 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
718 # on d0 to 0, so d0 goes away (despite that it's in an older
719 # generation) and d0's __del__ triggers. That in turn materializes
720 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
721
722 # We want to let gc happen "naturally", to preserve the distinction
723 # between generations.
724 detector = GC_Detector()
725 junk = []
726 i = 0
727 while not detector.gc_happened:
728 i += 1
729 if i > 10000:
730 self.fail("gc didn't happen after 10000 iterations")
731 self.assertEqual(len(ouch), 0)
732 junk.append([]) # this will eventually trigger gc
733
734 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
735 for x in ouch:
736 # If __del__ resurrected c2, the instance would be damaged, with an
737 # empty __dict__.
738 self.assertEqual(x, None)
739
740def test_main():
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000741 enabled = gc.isenabled()
742 gc.disable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000743 assert not gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000744 debug = gc.get_debug()
745 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000746
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000747 try:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000748 gc.collect() # Delete 2nd generation garbage
749 run_unittest(GCTests, GCTogglingTests)
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000750 finally:
751 gc.set_debug(debug)
752 # test gc.enable() even if GC is disabled by default
753 if verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000754 print("restoring automatic collection")
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000755 # make sure to always test gc.enable()
756 gc.enable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000757 assert gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000758 if not enabled:
759 gc.disable()
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000760
Guido van Rossumd8faa362007-04-27 19:54:29 +0000761if __name__ == "__main__":
762 test_main()