blob: 100c76729c2350e651c0a35cd90a5bdbb0fbe597 [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
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00004import gc
Tim Petersead8b7a2004-10-30 23:09:22 +00005import weakref
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00006
Guido van Rossumd8faa362007-04-27 19:54:29 +00007### Support code
8###############################################################################
Tim Peters0f81ab62003-04-08 16:39:48 +00009
Tim Petersead8b7a2004-10-30 23:09:22 +000010# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
11# cyclic gc.
12
13# An instance of C1055820 has a self-loop, so becomes cyclic trash when
14# unreachable.
15class C1055820(object):
16 def __init__(self, i):
17 self.i = i
18 self.loop = self
19
20class GC_Detector(object):
21 # Create an instance I. Then gc hasn't happened again so long as
22 # I.gc_happened is false.
23
24 def __init__(self):
25 self.gc_happened = False
26
27 def it_happened(ignored):
28 self.gc_happened = True
29
30 # Create a piece of cyclic trash that triggers it_happened when
31 # gc collects it.
32 self.wr = weakref.ref(C1055820(666), it_happened)
33
Tim Petersead8b7a2004-10-30 23:09:22 +000034
Guido van Rossumd8faa362007-04-27 19:54:29 +000035### Tests
36###############################################################################
Tim Petersead8b7a2004-10-30 23:09:22 +000037
Guido van Rossumd8faa362007-04-27 19:54:29 +000038class GCTests(unittest.TestCase):
39 def test_list(self):
40 l = []
41 l.append(l)
42 gc.collect()
43 del l
44 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000045
Guido van Rossumd8faa362007-04-27 19:54:29 +000046 def test_dict(self):
47 d = {}
48 d[1] = d
49 gc.collect()
50 del d
51 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000052
Guido van Rossumd8faa362007-04-27 19:54:29 +000053 def test_tuple(self):
54 # since tuples are immutable we close the loop with a list
55 l = []
56 t = (l,)
57 l.append(t)
58 gc.collect()
59 del t
60 del l
61 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +000062
Guido van Rossumd8faa362007-04-27 19:54:29 +000063 def test_class(self):
64 class A:
65 pass
66 A.a = A
67 gc.collect()
68 del A
69 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000070
Guido van Rossumd8faa362007-04-27 19:54:29 +000071 def test_newstyleclass(self):
72 class A(object):
73 pass
74 gc.collect()
75 del A
76 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000077
Guido van Rossumd8faa362007-04-27 19:54:29 +000078 def test_instance(self):
79 class A:
80 pass
81 a = A()
82 a.a = a
83 gc.collect()
84 del a
85 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000086
Guido van Rossumd8faa362007-04-27 19:54:29 +000087 def test_newinstance(self):
88 class A(object):
89 pass
90 a = A()
91 a.a = a
92 gc.collect()
93 del a
94 self.assertNotEqual(gc.collect(), 0)
95 class B(list):
96 pass
97 class C(B, A):
98 pass
99 a = C()
100 a.a = a
101 gc.collect()
102 del a
103 self.assertNotEqual(gc.collect(), 0)
104 del B, C
105 self.assertNotEqual(gc.collect(), 0)
106 A.a = A()
107 del A
108 self.assertNotEqual(gc.collect(), 0)
109 self.assertEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000110
Guido van Rossumd8faa362007-04-27 19:54:29 +0000111 def test_method(self):
112 # Tricky: self.__init__ is a bound method, it references the instance.
113 class A:
114 def __init__(self):
115 self.init = self.__init__
116 a = A()
117 gc.collect()
118 del a
119 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000120
Guido van Rossumd8faa362007-04-27 19:54:29 +0000121 def test_finalizer(self):
122 # A() is uncollectable if it is part of a cycle, make sure it shows up
123 # in gc.garbage.
124 class A:
125 def __del__(self): pass
126 class B:
127 pass
128 a = A()
129 a.a = a
130 id_a = id(a)
131 b = B()
132 b.b = b
133 gc.collect()
134 del a
135 del b
136 self.assertNotEqual(gc.collect(), 0)
137 for obj in gc.garbage:
138 if id(obj) == id_a:
139 del obj.a
140 break
141 else:
142 self.fail("didn't find obj in garbage (finalizer)")
143 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000144
Guido van Rossumd8faa362007-04-27 19:54:29 +0000145 def test_finalizer_newclass(self):
146 # A() is uncollectable if it is part of a cycle, make sure it shows up
147 # in gc.garbage.
148 class A(object):
149 def __del__(self): pass
150 class B(object):
151 pass
152 a = A()
153 a.a = a
154 id_a = id(a)
155 b = B()
156 b.b = b
157 gc.collect()
158 del a
159 del b
160 self.assertNotEqual(gc.collect(), 0)
161 for obj in gc.garbage:
162 if id(obj) == id_a:
163 del obj.a
164 break
165 else:
166 self.fail("didn't find obj in garbage (finalizer)")
167 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000168
Guido van Rossumd8faa362007-04-27 19:54:29 +0000169 def test_function(self):
170 # Tricky: f -> d -> f, code should call d.clear() after the exec to
171 # break the cycle.
172 d = {}
173 exec("def f(): pass\n", d)
174 gc.collect()
175 del d
176 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +0000177
Guido van Rossumd8faa362007-04-27 19:54:29 +0000178 def test_frame(self):
179 def f():
180 frame = sys._getframe()
181 gc.collect()
182 f()
183 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +0000184
Guido van Rossumd8faa362007-04-27 19:54:29 +0000185 def test_saveall(self):
186 # Verify that cyclic garbage like lists show up in gc.garbage if the
187 # SAVEALL option is enabled.
Tim Petersead8b7a2004-10-30 23:09:22 +0000188
Guido van Rossumd8faa362007-04-27 19:54:29 +0000189 # First make sure we don't save away other stuff that just happens to
190 # be waiting for collection.
191 gc.collect()
192 # if this fails, someone else created immortal trash
193 self.assertEqual(gc.garbage, [])
194
195 L = []
196 L.append(L)
197 id_L = id(L)
198
199 debug = gc.get_debug()
200 gc.set_debug(debug | gc.DEBUG_SAVEALL)
201 del L
202 gc.collect()
203 gc.set_debug(debug)
204
205 self.assertEqual(len(gc.garbage), 1)
206 obj = gc.garbage.pop()
207 self.assertEqual(id(obj), id_L)
208
209 def test_del(self):
210 # __del__ methods can trigger collection, make this to happen
211 thresholds = gc.get_threshold()
212 gc.enable()
213 gc.set_threshold(1)
214
215 class A:
216 def __del__(self):
217 dir(self)
218 a = A()
219 del a
220
221 gc.disable()
222 gc.set_threshold(*thresholds)
223
224 def test_del_newclass(self):
225 # __del__ methods can trigger collection, make this to happen
226 thresholds = gc.get_threshold()
227 gc.enable()
228 gc.set_threshold(1)
229
230 class A(object):
231 def __del__(self):
232 dir(self)
233 a = A()
234 del a
235
236 gc.disable()
237 gc.set_threshold(*thresholds)
238
Christian Heimesa156e092008-02-16 07:38:31 +0000239 # The following two tests are fragile:
240 # They precisely count the number of allocations,
241 # which is highly implementation-dependent.
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200242 # For example, disposed tuples are not freed, but reused.
243 # To minimize variations, though, we first store the get_count() results
244 # and check them at the end.
Guido van Rossumd8faa362007-04-27 19:54:29 +0000245 def test_get_count(self):
246 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200247 a, b, c = gc.get_count()
248 x = []
249 d, e, f = gc.get_count()
250 self.assertEqual((b, c), (0, 0))
251 self.assertEqual((e, f), (0, 0))
252 # This is less fragile than asserting that a equals 0.
253 self.assertLess(a, 5)
254 # Between the two calls to get_count(), at least one object was
255 # created (the list).
256 self.assertGreater(d, a)
Guido van Rossumd8faa362007-04-27 19:54:29 +0000257
258 def test_collect_generations(self):
259 gc.collect()
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200260 # This object will "trickle" into generation N + 1 after
261 # each call to collect(N)
262 x = []
Guido van Rossumd8faa362007-04-27 19:54:29 +0000263 gc.collect(0)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200264 # x is now in gen 1
265 a, b, c = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000266 gc.collect(1)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200267 # x is now in gen 2
268 d, e, f = gc.get_count()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000269 gc.collect(2)
Antoine Pitroub35f29a2011-04-04 19:50:42 +0200270 # x is now in gen 3
271 g, h, i = gc.get_count()
272 # We don't check a, d, g since their exact values depends on
273 # internal implementation details of the interpreter.
274 self.assertEqual((b, c), (1, 0))
275 self.assertEqual((e, f), (0, 1))
276 self.assertEqual((h, i), (0, 0))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000277
278 def test_trashcan(self):
279 class Ouch:
280 n = 0
281 def __del__(self):
282 Ouch.n = Ouch.n + 1
283 if Ouch.n % 17 == 0:
284 gc.collect()
285
286 # "trashcan" is a hack to prevent stack overflow when deallocating
287 # very deeply nested tuples etc. It works in part by abusing the
288 # type pointer and refcount fields, and that can yield horrible
289 # problems when gc tries to traverse the structures.
290 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
291 # most likely die via segfault.
292
293 # Note: In 2.3 the possibility for compiling without cyclic gc was
294 # removed, and that in turn allows the trashcan mechanism to work
295 # via much simpler means (e.g., it never abuses the type pointer or
296 # refcount fields anymore). Since it's much less likely to cause a
297 # problem now, the various constants in this expensive (we force a lot
298 # of full collections) test are cut back from the 2.2 version.
299 gc.enable()
300 N = 150
301 for count in range(2):
302 t = []
303 for i in range(N):
304 t = [t, Ouch()]
305 u = []
306 for i in range(N):
307 u = [u, Ouch()]
308 v = {}
309 for i in range(N):
310 v = {1: v, 2: Ouch()}
311 gc.disable()
312
313 def test_boom(self):
314 class Boom:
315 def __getattr__(self, someattribute):
316 del self.attr
317 raise AttributeError
318
319 a = Boom()
320 b = Boom()
321 a.attr = b
322 b.attr = a
323
324 gc.collect()
325 garbagelen = len(gc.garbage)
326 del a, b
327 # a<->b are in a trash cycle now. Collection will invoke
328 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
329 # __getattr__ deletes the internal "attr" attributes as a side effect.
330 # That causes the trash cycle to get reclaimed via refcounts falling to
331 # 0, thus mutating the trash graph as a side effect of merely asking
332 # whether __del__ exists. This used to (before 2.3b1) crash Python.
333 # Now __getattr__ isn't called.
334 self.assertEqual(gc.collect(), 4)
335 self.assertEqual(len(gc.garbage), garbagelen)
336
337 def test_boom2(self):
338 class Boom2:
339 def __init__(self):
340 self.x = 0
341
342 def __getattr__(self, someattribute):
343 self.x += 1
344 if self.x > 1:
345 del self.attr
346 raise AttributeError
347
348 a = Boom2()
349 b = Boom2()
350 a.attr = b
351 b.attr = a
352
353 gc.collect()
354 garbagelen = len(gc.garbage)
355 del a, b
356 # Much like test_boom(), except that __getattr__ doesn't break the
357 # cycle until the second time gc checks for __del__. As of 2.3b1,
358 # there isn't a second time, so this simply cleans up the trash cycle.
359 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
360 # reclaimed this way.
361 self.assertEqual(gc.collect(), 4)
362 self.assertEqual(len(gc.garbage), garbagelen)
363
364 def test_boom_new(self):
365 # boom__new and boom2_new are exactly like boom and boom2, except use
366 # new-style classes.
367
368 class Boom_New(object):
369 def __getattr__(self, someattribute):
370 del self.attr
371 raise AttributeError
372
373 a = Boom_New()
374 b = Boom_New()
375 a.attr = b
376 b.attr = a
377
378 gc.collect()
379 garbagelen = len(gc.garbage)
380 del a, b
381 self.assertEqual(gc.collect(), 4)
382 self.assertEqual(len(gc.garbage), garbagelen)
383
384 def test_boom2_new(self):
385 class Boom2_New(object):
386 def __init__(self):
387 self.x = 0
388
389 def __getattr__(self, someattribute):
390 self.x += 1
391 if self.x > 1:
392 del self.attr
393 raise AttributeError
394
395 a = Boom2_New()
396 b = Boom2_New()
397 a.attr = b
398 b.attr = a
399
400 gc.collect()
401 garbagelen = len(gc.garbage)
402 del a, b
403 self.assertEqual(gc.collect(), 4)
404 self.assertEqual(len(gc.garbage), garbagelen)
405
406 def test_get_referents(self):
407 alist = [1, 3, 5]
408 got = gc.get_referents(alist)
409 got.sort()
410 self.assertEqual(got, alist)
411
412 atuple = tuple(alist)
413 got = gc.get_referents(atuple)
414 got.sort()
415 self.assertEqual(got, alist)
416
417 adict = {1: 3, 5: 7}
418 expected = [1, 3, 5, 7]
419 got = gc.get_referents(adict)
420 got.sort()
421 self.assertEqual(got, expected)
422
423 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
424 got.sort()
Guido van Rossum805365e2007-05-07 22:24:25 +0000425 self.assertEqual(got, [0, 0] + list(range(5)))
Guido van Rossumd8faa362007-04-27 19:54:29 +0000426
427 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
428
Antoine Pitrou3a652b12009-03-23 18:52:06 +0000429 def test_is_tracked(self):
430 # Atomic built-in types are not tracked, user-defined objects and
431 # mutable containers are.
432 # NOTE: types with special optimizations (e.g. tuple) have tests
433 # in their own test files instead.
434 self.assertFalse(gc.is_tracked(None))
435 self.assertFalse(gc.is_tracked(1))
436 self.assertFalse(gc.is_tracked(1.0))
437 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
438 self.assertFalse(gc.is_tracked(True))
439 self.assertFalse(gc.is_tracked(False))
440 self.assertFalse(gc.is_tracked(b"a"))
441 self.assertFalse(gc.is_tracked("a"))
442 self.assertFalse(gc.is_tracked(bytearray(b"a")))
443 self.assertFalse(gc.is_tracked(type))
444 self.assertFalse(gc.is_tracked(int))
445 self.assertFalse(gc.is_tracked(object))
446 self.assertFalse(gc.is_tracked(object()))
447
448 class UserClass:
449 pass
450 self.assertTrue(gc.is_tracked(gc))
451 self.assertTrue(gc.is_tracked(UserClass))
452 self.assertTrue(gc.is_tracked(UserClass()))
453 self.assertTrue(gc.is_tracked([]))
454 self.assertTrue(gc.is_tracked(set()))
455
Guido van Rossumd8faa362007-04-27 19:54:29 +0000456 def test_bug1055820b(self):
457 # Corresponds to temp2b.py in the bug report.
458
459 ouch = []
460 def callback(ignored):
461 ouch[:] = [wr() for wr in WRs]
462
463 Cs = [C1055820(i) for i in range(2)]
464 WRs = [weakref.ref(c, callback) for c in Cs]
465 c = None
466
467 gc.collect()
468 self.assertEqual(len(ouch), 0)
469 # Make the two instances trash, and collect again. The bug was that
470 # the callback materialized a strong reference to an instance, but gc
471 # cleared the instance's dict anyway.
472 Cs = None
473 gc.collect()
474 self.assertEqual(len(ouch), 2) # else the callbacks didn't run
475 for x in ouch:
476 # If the callback resurrected one of these guys, the instance
477 # would be damaged, with an empty __dict__.
478 self.assertEqual(x, None)
479
Antoine Pitrou696e0352010-08-08 22:18:46 +0000480 def test_garbage_at_shutdown(self):
481 import subprocess
482 code = """if 1:
483 import gc
484 class X:
485 def __init__(self, name):
486 self.name = name
487 def __repr__(self):
488 return "<X %%r>" %% self.name
489 def __del__(self):
490 pass
491
492 x = X('first')
493 x.x = x
494 x.y = X('second')
495 del x
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000496 gc.set_debug(%s)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000497 """
498 def run_command(code):
Georg Brandl08be72d2010-10-24 15:11:22 +0000499 p = subprocess.Popen([sys.executable, "-Wd", "-c", code],
Antoine Pitrou696e0352010-08-08 22:18:46 +0000500 stdout=subprocess.PIPE,
501 stderr=subprocess.PIPE)
502 stdout, stderr = p.communicate()
Brian Curtin8291af22010-11-01 16:40:17 +0000503 p.stdout.close()
504 p.stderr.close()
Antoine Pitrou696e0352010-08-08 22:18:46 +0000505 self.assertEqual(p.returncode, 0)
506 self.assertEqual(stdout.strip(), b"")
507 return strip_python_stderr(stderr)
508
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000509 stderr = run_command(code % "0")
Georg Brandl08be72d2010-10-24 15:11:22 +0000510 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
511 b"shutdown; use", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000512 self.assertNotIn(b"<X 'first'>", stderr)
Antoine Pitrou696e0352010-08-08 22:18:46 +0000513 # With DEBUG_UNCOLLECTABLE, the garbage list gets printed
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000514 stderr = run_command(code % "gc.DEBUG_UNCOLLECTABLE")
Georg Brandl08be72d2010-10-24 15:11:22 +0000515 self.assertIn(b"ResourceWarning: gc: 2 uncollectable objects at "
516 b"shutdown", stderr)
Antoine Pitrouaee47562010-09-16 15:04:49 +0000517 self.assertTrue(
518 (b"[<X 'first'>, <X 'second'>]" in stderr) or
519 (b"[<X 'second'>, <X 'first'>]" in stderr), stderr)
Antoine Pitrou2ed94eb2010-09-14 09:48:39 +0000520 # With DEBUG_SAVEALL, no additional message should get printed
521 # (because gc.garbage also contains normally reclaimable cyclic
522 # references, and its elements get printed at runtime anyway).
523 stderr = run_command(code % "gc.DEBUG_SAVEALL")
524 self.assertNotIn(b"uncollectable objects at shutdown", stderr)
525
Antoine Pitrou696e0352010-08-08 22:18:46 +0000526
Guido van Rossumd8faa362007-04-27 19:54:29 +0000527class GCTogglingTests(unittest.TestCase):
528 def setUp(self):
529 gc.enable()
530
531 def tearDown(self):
532 gc.disable()
533
534 def test_bug1055820c(self):
535 # Corresponds to temp2c.py in the bug report. This is pretty
536 # elaborate.
537
538 c0 = C1055820(0)
539 # Move c0 into generation 2.
540 gc.collect()
541
542 c1 = C1055820(1)
543 c1.keep_c0_alive = c0
544 del c0.loop # now only c1 keeps c0 alive
545
546 c2 = C1055820(2)
547 c2wr = weakref.ref(c2) # no callback!
548
549 ouch = []
550 def callback(ignored):
Tim Petersead8b7a2004-10-30 23:09:22 +0000551 ouch[:] = [c2wr()]
552
Guido van Rossumd8faa362007-04-27 19:54:29 +0000553 # The callback gets associated with a wr on an object in generation 2.
554 c0wr = weakref.ref(c0, callback)
Tim Petersead8b7a2004-10-30 23:09:22 +0000555
Guido van Rossumd8faa362007-04-27 19:54:29 +0000556 c0 = c1 = c2 = None
Tim Petersead8b7a2004-10-30 23:09:22 +0000557
Guido van Rossumd8faa362007-04-27 19:54:29 +0000558 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
559 # generation 2. The only thing keeping it alive is that c1 points to
560 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
561 # global weakref to c2 (c2wr), but that weakref has no callback.
562 # There's also a global weakref to c0 (c0wr), and that does have a
563 # callback, and that callback references c2 via c2wr().
564 #
565 # c0 has a wr with callback, which references c2wr
566 # ^
567 # |
568 # | Generation 2 above dots
569 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
570 # | Generation 0 below dots
571 # |
572 # |
573 # ^->c1 ^->c2 has a wr but no callback
574 # | | | |
575 # <--v <--v
576 #
577 # So this is the nightmare: when generation 0 gets collected, we see
578 # that c2 has a callback-free weakref, and c1 doesn't even have a
579 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
580 # the only object that has a weakref with a callback. gc clears c1
581 # and c2. Clearing c1 has the side effect of dropping the refcount on
582 # c0 to 0, so c0 goes away (despite that it's in an older generation)
583 # and c0's wr callback triggers. That in turn materializes a reference
584 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
Tim Petersead8b7a2004-10-30 23:09:22 +0000585
Guido van Rossumd8faa362007-04-27 19:54:29 +0000586 # We want to let gc happen "naturally", to preserve the distinction
587 # between generations.
588 junk = []
589 i = 0
590 detector = GC_Detector()
591 while not detector.gc_happened:
592 i += 1
593 if i > 10000:
594 self.fail("gc didn't happen after 10000 iterations")
595 self.assertEqual(len(ouch), 0)
596 junk.append([]) # this will eventually trigger gc
Tim Petersead8b7a2004-10-30 23:09:22 +0000597
Guido van Rossumd8faa362007-04-27 19:54:29 +0000598 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
599 for x in ouch:
600 # If the callback resurrected c2, the instance would be damaged,
601 # with an empty __dict__.
602 self.assertEqual(x, None)
Tim Petersead8b7a2004-10-30 23:09:22 +0000603
Guido van Rossumd8faa362007-04-27 19:54:29 +0000604 def test_bug1055820d(self):
605 # Corresponds to temp2d.py in the bug report. This is very much like
606 # test_bug1055820c, but uses a __del__ method instead of a weakref
607 # callback to sneak in a resurrection of cyclic trash.
Tim Petersead8b7a2004-10-30 23:09:22 +0000608
Guido van Rossumd8faa362007-04-27 19:54:29 +0000609 ouch = []
610 class D(C1055820):
611 def __del__(self):
612 ouch[:] = [c2wr()]
Tim Petersead8b7a2004-10-30 23:09:22 +0000613
Guido van Rossumd8faa362007-04-27 19:54:29 +0000614 d0 = D(0)
615 # Move all the above into generation 2.
616 gc.collect()
Tim Petersead8b7a2004-10-30 23:09:22 +0000617
Guido van Rossumd8faa362007-04-27 19:54:29 +0000618 c1 = C1055820(1)
619 c1.keep_d0_alive = d0
620 del d0.loop # now only c1 keeps d0 alive
Tim Petersead8b7a2004-10-30 23:09:22 +0000621
Guido van Rossumd8faa362007-04-27 19:54:29 +0000622 c2 = C1055820(2)
623 c2wr = weakref.ref(c2) # no callback!
Tim Petersead8b7a2004-10-30 23:09:22 +0000624
Guido van Rossumd8faa362007-04-27 19:54:29 +0000625 d0 = c1 = c2 = None
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000626
Guido van Rossumd8faa362007-04-27 19:54:29 +0000627 # What we've set up: d0, c1, and c2 are all trash now. d0 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
630 # a global weakref to c2 (c2wr), but that weakref has no callback.
631 # There are no other weakrefs.
632 #
633 # d0 has a __del__ method that references c2wr
634 # ^
635 # |
636 # | Generation 2 above dots
637 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
638 # | Generation 0 below dots
639 # |
640 # |
641 # ^->c1 ^->c2 has a wr but no callback
642 # | | | |
643 # <--v <--v
644 #
645 # So this is the nightmare: when generation 0 gets collected, we see
646 # that c2 has a callback-free weakref, and c1 doesn't even have a
647 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
648 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
649 # on d0 to 0, so d0 goes away (despite that it's in an older
650 # generation) and d0's __del__ triggers. That in turn materializes
651 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
652
653 # We want to let gc happen "naturally", to preserve the distinction
654 # between generations.
655 detector = GC_Detector()
656 junk = []
657 i = 0
658 while not detector.gc_happened:
659 i += 1
660 if i > 10000:
661 self.fail("gc didn't happen after 10000 iterations")
662 self.assertEqual(len(ouch), 0)
663 junk.append([]) # this will eventually trigger gc
664
665 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
666 for x in ouch:
667 # If __del__ resurrected c2, the instance would be damaged, with an
668 # empty __dict__.
669 self.assertEqual(x, None)
670
671def test_main():
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000672 enabled = gc.isenabled()
673 gc.disable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000674 assert not gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000675 debug = gc.get_debug()
676 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000677
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000678 try:
Guido van Rossumd8faa362007-04-27 19:54:29 +0000679 gc.collect() # Delete 2nd generation garbage
680 run_unittest(GCTests, GCTogglingTests)
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000681 finally:
682 gc.set_debug(debug)
683 # test gc.enable() even if GC is disabled by default
684 if verbose:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000685 print("restoring automatic collection")
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000686 # make sure to always test gc.enable()
687 gc.enable()
Guido van Rossumd8faa362007-04-27 19:54:29 +0000688 assert gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000689 if not enabled:
690 gc.disable()
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000691
Guido van Rossumd8faa362007-04-27 19:54:29 +0000692if __name__ == "__main__":
693 test_main()