|
4 | 4 | from concurrent.futures import ThreadPoolExecutor |
5 | 5 | from threading import Barrier, Thread |
6 | 6 | from unittest import TestCase |
| 7 | +import sys |
| 8 | +from test.support import import_helper, threading_helper |
7 | 9 |
|
8 | | -from test.support import threading_helper |
| 10 | +_testinternalcapi = import_helper.import_module("_testinternalcapi") |
9 | 11 |
|
10 | 12 |
|
11 | 13 |
|
@@ -84,6 +86,24 @@ def reader_func(): |
84 | 86 |
|
85 | 87 | self.run_one(writer_func, reader_func) |
86 | 88 |
|
| 89 | + def test_attr_cache_mortal(self): |
| 90 | + class C: |
| 91 | + x = object() |
| 92 | + |
| 93 | + class D(C): |
| 94 | + pass |
| 95 | + |
| 96 | + def writer_func(): |
| 97 | + for _ in range(3000): |
| 98 | + C.x = object() |
| 99 | + |
| 100 | + def reader_func(): |
| 101 | + for _ in range(3000): |
| 102 | + C.x |
| 103 | + D.x |
| 104 | + |
| 105 | + self.run_one(writer_func, reader_func) |
| 106 | + |
87 | 107 | def test___class___modification(self): |
88 | 108 | loops = 200 |
89 | 109 |
|
@@ -160,6 +180,51 @@ def reader(): |
160 | 180 |
|
161 | 181 | self.run_one(writer, reader) |
162 | 182 |
|
| 183 | + def test_per_type_cache_concurrent_reads(self): |
| 184 | + class C: |
| 185 | + pass |
| 186 | + |
| 187 | + names = [sys.intern(f"attr_{i}") for i in range( |
| 188 | + _testinternalcapi._Py_TYPECACHE_MINSIZE * 4)] |
| 189 | + for name in names: |
| 190 | + setattr(C, name, name) |
| 191 | + # Prime the cache. |
| 192 | + for name in names: |
| 193 | + getattr(C, name) |
| 194 | + |
| 195 | + lookup = _testinternalcapi.type_cache_lookup |
| 196 | + |
| 197 | + def reader(): |
| 198 | + for _ in range(500): |
| 199 | + for name in names: |
| 200 | + hit, value, _ = lookup(C, name) |
| 201 | + self.assertEqual(hit, 1, name) |
| 202 | + self.assertEqual(value, name) |
| 203 | + |
| 204 | + threading_helper.run_concurrently(reader, nthreads=NTHREADS) |
| 205 | + |
| 206 | + def test_per_type_cache_concurrent_invalidate(self): |
| 207 | + class C: |
| 208 | + x = "value" |
| 209 | + |
| 210 | + # Prime the cache. |
| 211 | + C.x |
| 212 | + hit, value, version = _testinternalcapi.type_cache_lookup(C, "x") |
| 213 | + self.assertEqual(hit, 1) |
| 214 | + self.assertIs(value, "value") |
| 215 | + self.assertGreater(version, 0) |
| 216 | + |
| 217 | + def reader(): |
| 218 | + for _ in range(10_000): |
| 219 | + self.assertIs(C.x, "value") |
| 220 | + |
| 221 | + def invalidator(): |
| 222 | + for _ in range(10_000): |
| 223 | + _testinternalcapi.type_cache_invalidate(C) |
| 224 | + |
| 225 | + workers = [invalidator] + [reader] * (NTHREADS - 1) |
| 226 | + threading_helper.run_concurrently(workers) |
| 227 | + |
163 | 228 | def test_race_type_attr_added(self): |
164 | 229 | NROUNDS = 50 |
165 | 230 | NSTOPPERS = 4 |
|
0 commit comments