test_gc.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import gc
  2. import sys
  3. import unittest
  4. import weakref
  5. import greenlet
  6. class GCTests(unittest.TestCase):
  7. def test_dead_circular_ref(self):
  8. o = weakref.ref(greenlet.greenlet(greenlet.getcurrent).switch())
  9. gc.collect()
  10. self.assertTrue(o() is None)
  11. self.assertFalse(gc.garbage, gc.garbage)
  12. if greenlet.GREENLET_USE_GC:
  13. # These only work with greenlet gc support
  14. def test_circular_greenlet(self):
  15. class circular_greenlet(greenlet.greenlet):
  16. pass
  17. o = circular_greenlet()
  18. o.self = o
  19. o = weakref.ref(o)
  20. gc.collect()
  21. self.assertTrue(o() is None)
  22. self.assertFalse(gc.garbage, gc.garbage)
  23. def test_inactive_ref(self):
  24. class inactive_greenlet(greenlet.greenlet):
  25. def __init__(self):
  26. greenlet.greenlet.__init__(self, run=self.run)
  27. def run(self):
  28. pass
  29. o = inactive_greenlet()
  30. o = weakref.ref(o)
  31. gc.collect()
  32. self.assertTrue(o() is None)
  33. self.assertFalse(gc.garbage, gc.garbage)
  34. def test_finalizer_crash(self):
  35. # This test is designed to crash when active greenlets
  36. # are made garbage collectable, until the underlying
  37. # problem is resolved. How does it work:
  38. # - order of object creation is important
  39. # - array is created first, so it is moved to unreachable first
  40. # - we create a cycle between a greenlet and this array
  41. # - we create an object that participates in gc, is only
  42. # referenced by a greenlet, and would corrupt gc lists
  43. # on destruction, the easiest is to use an object with
  44. # a finalizer
  45. # - because array is the first object in unreachable it is
  46. # cleared first, which causes all references to greenlet
  47. # to disappear and causes greenlet to be destroyed, but since
  48. # it is still live it causes a switch during gc, which causes
  49. # an object with finalizer to be destroyed, which causes stack
  50. # corruption and then a crash
  51. class object_with_finalizer(object):
  52. def __del__(self):
  53. pass
  54. array = []
  55. parent = greenlet.getcurrent()
  56. def greenlet_body():
  57. greenlet.getcurrent().object = object_with_finalizer()
  58. try:
  59. parent.switch()
  60. finally:
  61. del greenlet.getcurrent().object
  62. g = greenlet.greenlet(greenlet_body)
  63. g.array = array
  64. array.append(g)
  65. g.switch()
  66. del array
  67. del g
  68. greenlet.getcurrent()
  69. gc.collect()