MCPcopy Index your code
hub / github.com/RustPython/RustPython / test_trash_weakref_clear

Method test_trash_weakref_clear

Lib/test/test_gc.py:1070–1125  ·  view source on GitHub ↗
(self)

Source from the content-addressed store, hash-verified

1068 @unittest.skipIf(ContainerNoGC is None,
1069 'requires ContainerNoGC extension type')
1070 def test_trash_weakref_clear(self):
1071 # Test that trash weakrefs are properly cleared (bpo-38006).
1072 #
1073 # Structure we are creating:
1074 #
1075 # Z <- Y <- A--+--> WZ -> C
1076 # ^ |
1077 # +--+
1078 # where:
1079 # WZ is a weakref to Z with callback C
1080 # Y doesn't implement tp_traverse
1081 # A contains a reference to itself, Y and WZ
1082 #
1083 # A, Y, Z, WZ are all trash. The GC doesn't know that Z is trash
1084 # because Y does not implement tp_traverse. To show the bug, WZ needs
1085 # to live long enough so that Z is deallocated before it. Then, if
1086 # gcmodule is buggy, when Z is being deallocated, C will run.
1087 #
1088 # To ensure WZ lives long enough, we put it in a second reference
1089 # cycle. That trick only works due to the ordering of the GC prev/next
1090 # linked lists. So, this test is a bit fragile.
1091 #
1092 # The bug reported in bpo-38006 is caused because the GC did not
1093 # clear WZ before starting the process of calling tp_clear on the
1094 # trash. Normally, handle_weakrefs() would find the weakref via Z and
1095 # clear it. However, since the GC cannot find Z, WR is not cleared and
1096 # it can execute during delete_garbage(). That can lead to disaster
1097 # since the callback might tinker with objects that have already had
1098 # tp_clear called on them (leaving them in possibly invalid states).
1099
1100 callback = unittest.mock.Mock()
1101
1102 class A:
1103 __slots__ = ['a', 'y', 'wz']
1104
1105 class Z:
1106 pass
1107
1108 # setup required object graph, as described above
1109 a = A()
1110 a.a = a
1111 a.y = ContainerNoGC(Z())
1112 a.wz = weakref.ref(a.y.value, callback)
1113 # create second cycle to keep WZ alive longer
1114 wr_cycle = [a.wz]
1115 wr_cycle.append(wr_cycle)
1116 # ensure trash unrelated to this test is gone
1117 gc.collect()
1118 gc.disable()
1119 # release references and create trash
1120 del a, wr_cycle
1121 gc.collect()
1122 # if called, it means there is a bug in the GC. The weakref should be
1123 # cleared before Z dies.
1124 callback.assert_not_called()
1125 gc.enable()
1126
1127 @cpython_only

Callers

nothing calls this directly

Calls 7

collectMethod · 0.80
assert_not_calledMethod · 0.80
AClass · 0.70
ZClass · 0.70
appendMethod · 0.45
disableMethod · 0.45
enableMethod · 0.45

Tested by

no test coverage detected