test_gc.py 2.9 KB

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