mirror of
https://github.com/python/cpython
synced 2026-11-06 09:02:40 +03:00
(cherry picked from commit f674db1803)
Co-authored-by: Abhishek Tiwari <Abhi210@users.noreply.github.com>
92 lines
2.4 KiB
Python
92 lines
2.4 KiB
Python
"""Regression tests for weakref races in subtype_getweakref() (gh-149816)."""
|
|
|
|
import gc
|
|
import sys
|
|
import unittest
|
|
import weakref
|
|
|
|
from test.support import threading_helper
|
|
|
|
|
|
@threading_helper.requires_working_threading()
|
|
class TestWeakrefRaces(unittest.TestCase):
|
|
|
|
ITERATIONS = 5_000
|
|
|
|
def test_getweakref_no_crash(self):
|
|
"""obj.__weakref__ racing with concurrent weakref creation/destruction."""
|
|
class Target:
|
|
pass
|
|
|
|
obj = Target()
|
|
|
|
def reader():
|
|
for _ in range(self.ITERATIONS):
|
|
ref = obj.__weakref__
|
|
self.assertIn(type(ref), (type(None), weakref.ref))
|
|
if ref is not None:
|
|
self.assertIn(ref(), (None, obj))
|
|
|
|
def mutator():
|
|
for _ in range(self.ITERATIONS):
|
|
weakref.ref(obj, lambda _: None)
|
|
|
|
threading_helper.run_concurrently([reader, mutator])
|
|
|
|
def test_getweakref_return_type(self):
|
|
"""__weakref__ must return None or a live weakref.ref."""
|
|
class Target:
|
|
pass
|
|
|
|
obj = Target()
|
|
|
|
self.assertIsNone(obj.__weakref__)
|
|
|
|
ref = weakref.ref(obj)
|
|
result = obj.__weakref__
|
|
self.assertIsInstance(result, weakref.ref)
|
|
self.assertIs(result(), obj)
|
|
|
|
del ref, result
|
|
gc.collect()
|
|
self.assertIsNone(obj.__weakref__)
|
|
|
|
def test_getweakref_no_refcount_leak(self):
|
|
"""Each __weakref__ access must not inflate the weakref's refcount."""
|
|
class Target:
|
|
pass
|
|
|
|
obj = Target()
|
|
ref = weakref.ref(obj)
|
|
before = sys.getrefcount(ref)
|
|
|
|
for _ in range(1_000):
|
|
r = obj.__weakref__
|
|
del r
|
|
|
|
gc.collect()
|
|
self.assertEqual(sys.getrefcount(ref), before)
|
|
|
|
def test_getweakref_many_readers(self):
|
|
"""Multiple concurrent readers and mutators must not crash."""
|
|
class Target:
|
|
pass
|
|
|
|
obj = Target()
|
|
n = 4
|
|
iters = self.ITERATIONS // n
|
|
|
|
def reader():
|
|
for _ in range(iters):
|
|
ref = obj.__weakref__
|
|
self.assertIn(type(ref), (type(None), weakref.ref))
|
|
|
|
def mutator():
|
|
for _ in range(iters):
|
|
weakref.ref(obj, lambda _: None)
|
|
|
|
threading_helper.run_concurrently([reader] * n + [mutator] * n)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|