blob: 7e47b2d3a27baef1f86aa59874b5621b05cb3ef6 [file] [log] [blame]
Collin Winterfef1dcf2007-04-06 20:00:05 +00001import unittest
Victor Stinner7b4ba622017-10-17 02:25:23 -07002from test.support import (verbose, run_unittest, start_threads,
3 requires_type_collecting)
Neil Schemenauer88c761a2001-07-12 13:25:53 +00004import sys
Antoine Pitrou58098a72012-09-06 00:59:49 +02005import time
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00006import gc
Tim Petersead8b7a2004-10-30 23:09:22 +00007import weakref
Jeremy Hyltonc5007aa2000-06-30 05:02:53 +00008
Antoine Pitrou58098a72012-09-06 00:59:49 +02009try:
10 import threading
11except ImportError:
12 threading = None
13
Collin Winterfef1dcf2007-04-06 20:00:05 +000014### Support code
15###############################################################################
Neal Norwitz0d4c06e2007-04-25 06:30:05 +000016
Tim Petersead8b7a2004-10-30 23:09:22 +000017# Bug 1055820 has several tests of longstanding bugs involving weakrefs and
18# cyclic gc.
19
20# An instance of C1055820 has a self-loop, so becomes cyclic trash when
21# unreachable.
22class C1055820(object):
23 def __init__(self, i):
24 self.i = i
25 self.loop = self
26
27class GC_Detector(object):
28 # Create an instance I. Then gc hasn't happened again so long as
29 # I.gc_happened is false.
30
31 def __init__(self):
32 self.gc_happened = False
33
34 def it_happened(ignored):
35 self.gc_happened = True
36
37 # Create a piece of cyclic trash that triggers it_happened when
38 # gc collects it.
39 self.wr = weakref.ref(C1055820(666), it_happened)
40
Tim Petersead8b7a2004-10-30 23:09:22 +000041
Neal Norwitz0d4c06e2007-04-25 06:30:05 +000042### Tests
Collin Winterfef1dcf2007-04-06 20:00:05 +000043###############################################################################
Neal Norwitz0d4c06e2007-04-25 06:30:05 +000044
Collin Winterfef1dcf2007-04-06 20:00:05 +000045class GCTests(unittest.TestCase):
46 def test_list(self):
47 l = []
48 l.append(l)
49 gc.collect()
50 del l
51 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000052
Collin Winterfef1dcf2007-04-06 20:00:05 +000053 def test_dict(self):
54 d = {}
55 d[1] = d
56 gc.collect()
57 del d
58 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +000059
Collin Winterfef1dcf2007-04-06 20:00:05 +000060 def test_tuple(self):
61 # since tuples are immutable we close the loop with a list
62 l = []
63 t = (l,)
64 l.append(t)
65 gc.collect()
66 del t
67 del l
68 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +000069
Collin Winterfef1dcf2007-04-06 20:00:05 +000070 def test_class(self):
71 class A:
72 pass
73 A.a = A
74 gc.collect()
75 del A
76 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000077
Collin Winterfef1dcf2007-04-06 20:00:05 +000078 def test_newstyleclass(self):
79 class A(object):
80 pass
81 gc.collect()
82 del A
83 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000084
Collin Winterfef1dcf2007-04-06 20:00:05 +000085 def test_instance(self):
86 class A:
87 pass
88 a = A()
89 a.a = a
90 gc.collect()
91 del a
92 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +000093
Victor Stinner7b4ba622017-10-17 02:25:23 -070094 @requires_type_collecting
Collin Winterfef1dcf2007-04-06 20:00:05 +000095 def test_newinstance(self):
96 class A(object):
97 pass
98 a = A()
99 a.a = a
100 gc.collect()
101 del a
102 self.assertNotEqual(gc.collect(), 0)
103 class B(list):
104 pass
105 class C(B, A):
106 pass
107 a = C()
108 a.a = a
109 gc.collect()
110 del a
111 self.assertNotEqual(gc.collect(), 0)
112 del B, C
113 self.assertNotEqual(gc.collect(), 0)
114 A.a = A()
115 del A
116 self.assertNotEqual(gc.collect(), 0)
117 self.assertEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000118
Collin Winterfef1dcf2007-04-06 20:00:05 +0000119 def test_method(self):
120 # Tricky: self.__init__ is a bound method, it references the instance.
121 class A:
122 def __init__(self):
123 self.init = self.__init__
124 a = A()
125 gc.collect()
126 del a
127 self.assertNotEqual(gc.collect(), 0)
Tim Petersead8b7a2004-10-30 23:09:22 +0000128
Collin Winterfef1dcf2007-04-06 20:00:05 +0000129 def test_finalizer(self):
130 # A() is uncollectable if it is part of a cycle, make sure it shows up
131 # in gc.garbage.
132 class A:
133 def __del__(self): pass
134 class B:
135 pass
136 a = A()
137 a.a = a
138 id_a = id(a)
139 b = B()
140 b.b = b
141 gc.collect()
142 del a
143 del b
144 self.assertNotEqual(gc.collect(), 0)
145 for obj in gc.garbage:
146 if id(obj) == id_a:
147 del obj.a
148 break
149 else:
150 self.fail("didn't find obj in garbage (finalizer)")
151 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000152
Collin Winterfef1dcf2007-04-06 20:00:05 +0000153 def test_finalizer_newclass(self):
154 # A() is uncollectable if it is part of a cycle, make sure it shows up
155 # in gc.garbage.
156 class A(object):
157 def __del__(self): pass
158 class B(object):
159 pass
160 a = A()
161 a.a = a
162 id_a = id(a)
163 b = B()
164 b.b = b
165 gc.collect()
166 del a
167 del b
168 self.assertNotEqual(gc.collect(), 0)
169 for obj in gc.garbage:
170 if id(obj) == id_a:
171 del obj.a
172 break
173 else:
174 self.fail("didn't find obj in garbage (finalizer)")
175 gc.garbage.remove(obj)
Tim Petersead8b7a2004-10-30 23:09:22 +0000176
Collin Winterfef1dcf2007-04-06 20:00:05 +0000177 def test_function(self):
178 # Tricky: f -> d -> f, code should call d.clear() after the exec to
179 # break the cycle.
180 d = {}
181 exec("def f(): pass\n") in d
182 gc.collect()
183 del d
184 self.assertEqual(gc.collect(), 2)
Tim Petersead8b7a2004-10-30 23:09:22 +0000185
Collin Winterfef1dcf2007-04-06 20:00:05 +0000186 def test_frame(self):
187 def f():
188 frame = sys._getframe()
189 gc.collect()
190 f()
191 self.assertEqual(gc.collect(), 1)
Tim Petersead8b7a2004-10-30 23:09:22 +0000192
Collin Winterfef1dcf2007-04-06 20:00:05 +0000193 def test_saveall(self):
194 # Verify that cyclic garbage like lists show up in gc.garbage if the
195 # SAVEALL option is enabled.
Tim Petersead8b7a2004-10-30 23:09:22 +0000196
Collin Winterfef1dcf2007-04-06 20:00:05 +0000197 # First make sure we don't save away other stuff that just happens to
198 # be waiting for collection.
199 gc.collect()
200 # if this fails, someone else created immortal trash
201 self.assertEqual(gc.garbage, [])
Tim Petersead8b7a2004-10-30 23:09:22 +0000202
Collin Winterfef1dcf2007-04-06 20:00:05 +0000203 L = []
204 L.append(L)
205 id_L = id(L)
206
207 debug = gc.get_debug()
208 gc.set_debug(debug | gc.DEBUG_SAVEALL)
209 del L
210 gc.collect()
211 gc.set_debug(debug)
212
213 self.assertEqual(len(gc.garbage), 1)
214 obj = gc.garbage.pop()
215 self.assertEqual(id(obj), id_L)
216
217 def test_del(self):
218 # __del__ methods can trigger collection, make this to happen
219 thresholds = gc.get_threshold()
220 gc.enable()
221 gc.set_threshold(1)
222
223 class A:
224 def __del__(self):
225 dir(self)
226 a = A()
227 del a
228
229 gc.disable()
230 gc.set_threshold(*thresholds)
231
232 def test_del_newclass(self):
233 # __del__ methods can trigger collection, make this to happen
234 thresholds = gc.get_threshold()
235 gc.enable()
236 gc.set_threshold(1)
237
238 class A(object):
239 def __del__(self):
240 dir(self)
241 a = A()
242 del a
243
244 gc.disable()
245 gc.set_threshold(*thresholds)
246
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000247 # The following two tests are fragile:
248 # They precisely count the number of allocations,
249 # which is highly implementation-dependent.
250 # For example:
251 # - disposed tuples are not freed, but reused
252 # - the call to assertEqual somehow avoids building its args tuple
Collin Winterfef1dcf2007-04-06 20:00:05 +0000253 def test_get_count(self):
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000254 # Avoid future allocation of method object
Gregory P. Smith28399852009-03-31 16:54:10 +0000255 assertEqual = self._baseAssertEqual
Collin Winterfef1dcf2007-04-06 20:00:05 +0000256 gc.collect()
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000257 assertEqual(gc.get_count(), (0, 0, 0))
Collin Winterfef1dcf2007-04-06 20:00:05 +0000258 a = dict()
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000259 # since gc.collect(), we created two objects:
260 # the dict, and the tuple returned by get_count()
261 assertEqual(gc.get_count(), (2, 0, 0))
Collin Winterfef1dcf2007-04-06 20:00:05 +0000262
263 def test_collect_generations(self):
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000264 # Avoid future allocation of method object
265 assertEqual = self.assertEqual
Collin Winterfef1dcf2007-04-06 20:00:05 +0000266 gc.collect()
267 a = dict()
268 gc.collect(0)
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000269 assertEqual(gc.get_count(), (0, 1, 0))
Collin Winterfef1dcf2007-04-06 20:00:05 +0000270 gc.collect(1)
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000271 assertEqual(gc.get_count(), (0, 0, 1))
Collin Winterfef1dcf2007-04-06 20:00:05 +0000272 gc.collect(2)
Amaury Forgeot d'Arcd8bcbf22008-02-15 22:44:20 +0000273 assertEqual(gc.get_count(), (0, 0, 0))
Collin Winterfef1dcf2007-04-06 20:00:05 +0000274
275 def test_trashcan(self):
276 class Ouch:
277 n = 0
278 def __del__(self):
279 Ouch.n = Ouch.n + 1
280 if Ouch.n % 17 == 0:
281 gc.collect()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000282
Collin Winterfef1dcf2007-04-06 20:00:05 +0000283 # "trashcan" is a hack to prevent stack overflow when deallocating
284 # very deeply nested tuples etc. It works in part by abusing the
285 # type pointer and refcount fields, and that can yield horrible
286 # problems when gc tries to traverse the structures.
287 # If this test fails (as it does in 2.0, 2.1 and 2.2), it will
288 # most likely die via segfault.
289
290 # Note: In 2.3 the possibility for compiling without cyclic gc was
291 # removed, and that in turn allows the trashcan mechanism to work
292 # via much simpler means (e.g., it never abuses the type pointer or
293 # refcount fields anymore). Since it's much less likely to cause a
294 # problem now, the various constants in this expensive (we force a lot
295 # of full collections) test are cut back from the 2.2 version.
296 gc.enable()
297 N = 150
298 for count in range(2):
299 t = []
300 for i in range(N):
301 t = [t, Ouch()]
302 u = []
303 for i in range(N):
304 u = [u, Ouch()]
305 v = {}
306 for i in range(N):
307 v = {1: v, 2: Ouch()}
308 gc.disable()
309
Antoine Pitrou58098a72012-09-06 00:59:49 +0200310 @unittest.skipUnless(threading, "test meaningless on builds without threads")
311 def test_trashcan_threads(self):
312 # Issue #13992: trashcan mechanism should be thread-safe
313 NESTING = 60
314 N_THREADS = 2
315
316 def sleeper_gen():
317 """A generator that releases the GIL when closed or dealloc'ed."""
318 try:
319 yield
320 finally:
321 time.sleep(0.000001)
322
323 class C(list):
324 # Appending to a list is atomic, which avoids the use of a lock.
325 inits = []
326 dels = []
327 def __init__(self, alist):
328 self[:] = alist
329 C.inits.append(None)
330 def __del__(self):
331 # This __del__ is called by subtype_dealloc().
332 C.dels.append(None)
333 # `g` will release the GIL when garbage-collected. This
334 # helps assert subtype_dealloc's behaviour when threads
335 # switch in the middle of it.
336 g = sleeper_gen()
337 next(g)
338 # Now that __del__ is finished, subtype_dealloc will proceed
339 # to call list_dealloc, which also uses the trashcan mechanism.
340
341 def make_nested():
342 """Create a sufficiently nested container object so that the
343 trashcan mechanism is invoked when deallocating it."""
344 x = C([])
345 for i in range(NESTING):
346 x = [C([x])]
347 del x
348
349 def run_thread():
350 """Exercise make_nested() in a loop."""
351 while not exit:
352 make_nested()
353
354 old_checkinterval = sys.getcheckinterval()
355 sys.setcheckinterval(3)
356 try:
Serhiy Storchakabd8c6292015-04-01 12:56:39 +0300357 exit = []
Antoine Pitrou58098a72012-09-06 00:59:49 +0200358 threads = []
359 for i in range(N_THREADS):
360 t = threading.Thread(target=run_thread)
361 threads.append(t)
Serhiy Storchakabd8c6292015-04-01 12:56:39 +0300362 with start_threads(threads, lambda: exit.append(1)):
Serhiy Storchaka53ea1622015-03-28 20:38:48 +0200363 time.sleep(1.0)
Antoine Pitrou58098a72012-09-06 00:59:49 +0200364 finally:
365 sys.setcheckinterval(old_checkinterval)
366 gc.collect()
367 self.assertEqual(len(C.inits), len(C.dels))
368
Collin Winterfef1dcf2007-04-06 20:00:05 +0000369 def test_boom(self):
370 class Boom:
371 def __getattr__(self, someattribute):
372 del self.attr
373 raise AttributeError
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000374
Collin Winterfef1dcf2007-04-06 20:00:05 +0000375 a = Boom()
376 b = Boom()
377 a.attr = b
378 b.attr = a
379
380 gc.collect()
381 garbagelen = len(gc.garbage)
382 del a, b
383 # a<->b are in a trash cycle now. Collection will invoke
384 # Boom.__getattr__ (to see whether a and b have __del__ methods), and
385 # __getattr__ deletes the internal "attr" attributes as a side effect.
386 # That causes the trash cycle to get reclaimed via refcounts falling to
387 # 0, thus mutating the trash graph as a side effect of merely asking
388 # whether __del__ exists. This used to (before 2.3b1) crash Python.
389 # Now __getattr__ isn't called.
390 self.assertEqual(gc.collect(), 4)
391 self.assertEqual(len(gc.garbage), garbagelen)
392
393 def test_boom2(self):
394 class Boom2:
395 def __init__(self):
396 self.x = 0
397
398 def __getattr__(self, someattribute):
399 self.x += 1
400 if self.x > 1:
401 del self.attr
402 raise AttributeError
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000403
Collin Winterfef1dcf2007-04-06 20:00:05 +0000404 a = Boom2()
405 b = Boom2()
406 a.attr = b
407 b.attr = a
408
409 gc.collect()
410 garbagelen = len(gc.garbage)
411 del a, b
412 # Much like test_boom(), except that __getattr__ doesn't break the
413 # cycle until the second time gc checks for __del__. As of 2.3b1,
414 # there isn't a second time, so this simply cleans up the trash cycle.
415 # We expect a, b, a.__dict__ and b.__dict__ (4 objects) to get
416 # reclaimed this way.
417 self.assertEqual(gc.collect(), 4)
418 self.assertEqual(len(gc.garbage), garbagelen)
419
420 def test_boom_new(self):
421 # boom__new and boom2_new are exactly like boom and boom2, except use
422 # new-style classes.
423
424 class Boom_New(object):
425 def __getattr__(self, someattribute):
426 del self.attr
427 raise AttributeError
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000428
Collin Winterfef1dcf2007-04-06 20:00:05 +0000429 a = Boom_New()
430 b = Boom_New()
431 a.attr = b
432 b.attr = a
433
434 gc.collect()
435 garbagelen = len(gc.garbage)
436 del a, b
437 self.assertEqual(gc.collect(), 4)
438 self.assertEqual(len(gc.garbage), garbagelen)
439
440 def test_boom2_new(self):
441 class Boom2_New(object):
442 def __init__(self):
443 self.x = 0
444
445 def __getattr__(self, someattribute):
446 self.x += 1
447 if self.x > 1:
448 del self.attr
449 raise AttributeError
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000450
Collin Winterfef1dcf2007-04-06 20:00:05 +0000451 a = Boom2_New()
452 b = Boom2_New()
453 a.attr = b
454 b.attr = a
455
456 gc.collect()
457 garbagelen = len(gc.garbage)
458 del a, b
459 self.assertEqual(gc.collect(), 4)
460 self.assertEqual(len(gc.garbage), garbagelen)
461
462 def test_get_referents(self):
463 alist = [1, 3, 5]
464 got = gc.get_referents(alist)
465 got.sort()
466 self.assertEqual(got, alist)
467
468 atuple = tuple(alist)
469 got = gc.get_referents(atuple)
470 got.sort()
471 self.assertEqual(got, alist)
472
473 adict = {1: 3, 5: 7}
474 expected = [1, 3, 5, 7]
475 got = gc.get_referents(adict)
476 got.sort()
477 self.assertEqual(got, expected)
478
479 got = gc.get_referents([1, 2], {3: 4}, (0, 0, 0))
480 got.sort()
481 self.assertEqual(got, [0, 0] + range(5))
482
483 self.assertEqual(gc.get_referents(1, 'a', 4j), [])
484
Antoine Pitrouf8387af2009-03-23 18:41:45 +0000485 def test_is_tracked(self):
486 # Atomic built-in types are not tracked, user-defined objects and
487 # mutable containers are.
488 # NOTE: types with special optimizations (e.g. tuple) have tests
489 # in their own test files instead.
490 self.assertFalse(gc.is_tracked(None))
491 self.assertFalse(gc.is_tracked(1))
492 self.assertFalse(gc.is_tracked(1.0))
493 self.assertFalse(gc.is_tracked(1.0 + 5.0j))
494 self.assertFalse(gc.is_tracked(True))
495 self.assertFalse(gc.is_tracked(False))
496 self.assertFalse(gc.is_tracked("a"))
497 self.assertFalse(gc.is_tracked(u"a"))
498 self.assertFalse(gc.is_tracked(bytearray("a")))
499 self.assertFalse(gc.is_tracked(type))
500 self.assertFalse(gc.is_tracked(int))
501 self.assertFalse(gc.is_tracked(object))
502 self.assertFalse(gc.is_tracked(object()))
503
504 class OldStyle:
505 pass
506 class NewStyle(object):
507 pass
508 self.assertTrue(gc.is_tracked(gc))
509 self.assertTrue(gc.is_tracked(OldStyle))
510 self.assertTrue(gc.is_tracked(OldStyle()))
511 self.assertTrue(gc.is_tracked(NewStyle))
512 self.assertTrue(gc.is_tracked(NewStyle()))
513 self.assertTrue(gc.is_tracked([]))
514 self.assertTrue(gc.is_tracked(set()))
515
Collin Winterfef1dcf2007-04-06 20:00:05 +0000516 def test_bug1055820b(self):
517 # Corresponds to temp2b.py in the bug report.
518
519 ouch = []
520 def callback(ignored):
521 ouch[:] = [wr() for wr in WRs]
522
523 Cs = [C1055820(i) for i in range(2)]
524 WRs = [weakref.ref(c, callback) for c in Cs]
525 c = None
526
527 gc.collect()
528 self.assertEqual(len(ouch), 0)
529 # Make the two instances trash, and collect again. The bug was that
530 # the callback materialized a strong reference to an instance, but gc
531 # cleared the instance's dict anyway.
532 Cs = None
533 gc.collect()
534 self.assertEqual(len(ouch), 2) # else the callbacks didn't run
535 for x in ouch:
536 # If the callback resurrected one of these guys, the instance
537 # would be damaged, with an empty __dict__.
538 self.assertEqual(x, None)
539
540class GCTogglingTests(unittest.TestCase):
541 def setUp(self):
542 gc.enable()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000543
Collin Winterfef1dcf2007-04-06 20:00:05 +0000544 def tearDown(self):
545 gc.disable()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000546
Collin Winterfef1dcf2007-04-06 20:00:05 +0000547 def test_bug1055820c(self):
548 # Corresponds to temp2c.py in the bug report. This is pretty
549 # elaborate.
550
551 c0 = C1055820(0)
552 # Move c0 into generation 2.
553 gc.collect()
554
555 c1 = C1055820(1)
556 c1.keep_c0_alive = c0
557 del c0.loop # now only c1 keeps c0 alive
558
559 c2 = C1055820(2)
560 c2wr = weakref.ref(c2) # no callback!
561
562 ouch = []
563 def callback(ignored):
Tim Petersead8b7a2004-10-30 23:09:22 +0000564 ouch[:] = [c2wr()]
565
Collin Winterfef1dcf2007-04-06 20:00:05 +0000566 # The callback gets associated with a wr on an object in generation 2.
567 c0wr = weakref.ref(c0, callback)
Tim Petersead8b7a2004-10-30 23:09:22 +0000568
Collin Winterfef1dcf2007-04-06 20:00:05 +0000569 c0 = c1 = c2 = None
Tim Petersead8b7a2004-10-30 23:09:22 +0000570
Collin Winterfef1dcf2007-04-06 20:00:05 +0000571 # What we've set up: c0, c1, and c2 are all trash now. c0 is in
572 # generation 2. The only thing keeping it alive is that c1 points to
573 # it. c1 and c2 are in generation 0, and are in self-loops. There's a
574 # global weakref to c2 (c2wr), but that weakref has no callback.
575 # There's also a global weakref to c0 (c0wr), and that does have a
576 # callback, and that callback references c2 via c2wr().
577 #
578 # c0 has a wr with callback, which references c2wr
579 # ^
580 # |
581 # | Generation 2 above dots
582 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
583 # | Generation 0 below dots
584 # |
585 # |
586 # ^->c1 ^->c2 has a wr but no callback
587 # | | | |
588 # <--v <--v
589 #
590 # So this is the nightmare: when generation 0 gets collected, we see
591 # that c2 has a callback-free weakref, and c1 doesn't even have a
592 # weakref. Collecting generation 0 doesn't see c0 at all, and c0 is
593 # the only object that has a weakref with a callback. gc clears c1
594 # and c2. Clearing c1 has the side effect of dropping the refcount on
595 # c0 to 0, so c0 goes away (despite that it's in an older generation)
596 # and c0's wr callback triggers. That in turn materializes a reference
597 # to c2 via c2wr(), but c2 gets cleared anyway by gc.
Tim Petersead8b7a2004-10-30 23:09:22 +0000598
Collin Winterfef1dcf2007-04-06 20:00:05 +0000599 # We want to let gc happen "naturally", to preserve the distinction
600 # between generations.
601 junk = []
602 i = 0
603 detector = GC_Detector()
604 while not detector.gc_happened:
605 i += 1
606 if i > 10000:
607 self.fail("gc didn't happen after 10000 iterations")
608 self.assertEqual(len(ouch), 0)
609 junk.append([]) # this will eventually trigger gc
Tim Petersead8b7a2004-10-30 23:09:22 +0000610
Collin Winterfef1dcf2007-04-06 20:00:05 +0000611 self.assertEqual(len(ouch), 1) # else the callback wasn't invoked
612 for x in ouch:
613 # If the callback resurrected c2, the instance would be damaged,
614 # with an empty __dict__.
615 self.assertEqual(x, None)
Tim Petersead8b7a2004-10-30 23:09:22 +0000616
Collin Winterfef1dcf2007-04-06 20:00:05 +0000617 def test_bug1055820d(self):
618 # Corresponds to temp2d.py in the bug report. This is very much like
619 # test_bug1055820c, but uses a __del__ method instead of a weakref
620 # callback to sneak in a resurrection of cyclic trash.
Tim Petersead8b7a2004-10-30 23:09:22 +0000621
Collin Winterfef1dcf2007-04-06 20:00:05 +0000622 ouch = []
623 class D(C1055820):
624 def __del__(self):
625 ouch[:] = [c2wr()]
Tim Petersead8b7a2004-10-30 23:09:22 +0000626
Collin Winterfef1dcf2007-04-06 20:00:05 +0000627 d0 = D(0)
628 # Move all the above into generation 2.
629 gc.collect()
Tim Petersead8b7a2004-10-30 23:09:22 +0000630
Collin Winterfef1dcf2007-04-06 20:00:05 +0000631 c1 = C1055820(1)
632 c1.keep_d0_alive = d0
633 del d0.loop # now only c1 keeps d0 alive
Tim Petersead8b7a2004-10-30 23:09:22 +0000634
Collin Winterfef1dcf2007-04-06 20:00:05 +0000635 c2 = C1055820(2)
636 c2wr = weakref.ref(c2) # no callback!
Tim Petersead8b7a2004-10-30 23:09:22 +0000637
Collin Winterfef1dcf2007-04-06 20:00:05 +0000638 d0 = c1 = c2 = None
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000639
Collin Winterfef1dcf2007-04-06 20:00:05 +0000640 # What we've set up: d0, c1, and c2 are all trash now. d0 is in
641 # generation 2. The only thing keeping it alive is that c1 points to
642 # it. c1 and c2 are in generation 0, and are in self-loops. There's
643 # a global weakref to c2 (c2wr), but that weakref has no callback.
644 # There are no other weakrefs.
645 #
646 # d0 has a __del__ method that references c2wr
647 # ^
648 # |
649 # | Generation 2 above dots
650 #. . . . . . . .|. . . . . . . . . . . . . . . . . . . . . . . .
651 # | Generation 0 below dots
652 # |
653 # |
654 # ^->c1 ^->c2 has a wr but no callback
655 # | | | |
656 # <--v <--v
657 #
658 # So this is the nightmare: when generation 0 gets collected, we see
659 # that c2 has a callback-free weakref, and c1 doesn't even have a
660 # weakref. Collecting generation 0 doesn't see d0 at all. gc clears
661 # c1 and c2. Clearing c1 has the side effect of dropping the refcount
662 # on d0 to 0, so d0 goes away (despite that it's in an older
663 # generation) and d0's __del__ triggers. That in turn materializes
664 # a reference to c2 via c2wr(), but c2 gets cleared anyway by gc.
665
666 # We want to let gc happen "naturally", to preserve the distinction
667 # between generations.
668 detector = GC_Detector()
669 junk = []
670 i = 0
671 while not detector.gc_happened:
672 i += 1
673 if i > 10000:
674 self.fail("gc didn't happen after 10000 iterations")
675 self.assertEqual(len(ouch), 0)
676 junk.append([]) # this will eventually trigger gc
677
678 self.assertEqual(len(ouch), 1) # else __del__ wasn't invoked
679 for x in ouch:
680 # If __del__ resurrected c2, the instance would be damaged, with an
681 # empty __dict__.
682 self.assertEqual(x, None)
683
684def test_main():
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000685 enabled = gc.isenabled()
686 gc.disable()
Collin Winterfef1dcf2007-04-06 20:00:05 +0000687 assert not gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000688 debug = gc.get_debug()
689 gc.set_debug(debug & ~gc.DEBUG_LEAK) # this test is supposed to leak
Vladimir Marangozovf9d20c32000-08-06 22:45:31 +0000690
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000691 try:
Collin Winterfef1dcf2007-04-06 20:00:05 +0000692 gc.collect() # Delete 2nd generation garbage
693 run_unittest(GCTests, GCTogglingTests)
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000694 finally:
695 gc.set_debug(debug)
696 # test gc.enable() even if GC is disabled by default
697 if verbose:
698 print "restoring automatic collection"
699 # make sure to always test gc.enable()
700 gc.enable()
Collin Winterfef1dcf2007-04-06 20:00:05 +0000701 assert gc.isenabled()
Neil Schemenauerfaae2662000-09-22 15:26:20 +0000702 if not enabled:
703 gc.disable()
Neal Norwitz0d4c06e2007-04-25 06:30:05 +0000704
Collin Winterfef1dcf2007-04-06 20:00:05 +0000705if __name__ == "__main__":
706 test_main()