|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | import contextvars |
| 4 | +import gc |
4 | 5 | import queue as stdlib_queue |
5 | 6 | import re |
6 | 7 | import sys |
|
29 | 30 | sleep_forever, |
30 | 31 | ) |
31 | 32 | from .._core._tests.test_ki import ki_self |
32 | | -from .._core._tests.tutil import slow |
| 33 | +from .._core._tests.tutil import gc_collect_harder, no_other_refs, slow |
33 | 34 | from .._threads import ( |
34 | 35 | active_thread_count, |
35 | 36 | current_default_thread_limiter, |
@@ -1141,3 +1142,58 @@ async def wait_no_threads_left() -> None: |
1141 | 1142 | async def test_wait_all_threads_completed_no_threads() -> None: |
1142 | 1143 | await wait_all_threads_completed() |
1143 | 1144 | assert active_thread_count() == 0 |
| 1145 | + |
| 1146 | + |
| 1147 | +@pytest.mark.skipif( |
| 1148 | + sys.implementation.name == "pypy", |
| 1149 | + reason=( |
| 1150 | + "gc.get_referrers is broken on PyPy (see " |
| 1151 | + "https://github.com/pypy/pypy/issues/5075)" |
| 1152 | + ), |
| 1153 | +) |
| 1154 | +async def test_run_sync_worker_references() -> None: |
| 1155 | + class Foo: |
| 1156 | + pass |
| 1157 | + |
| 1158 | + def foo(_: Foo) -> Foo: |
| 1159 | + return Foo() |
| 1160 | + |
| 1161 | + cvar = contextvars.ContextVar[Foo]("cvar") |
| 1162 | + contextval = Foo() |
| 1163 | + arg = Foo() |
| 1164 | + cvar.set(contextval) |
| 1165 | + v = await to_thread_run_sync(foo, arg) |
| 1166 | + |
| 1167 | + cvar.set(Foo()) |
| 1168 | + gc_collect_harder() |
| 1169 | + |
| 1170 | + assert gc.get_referrers(contextval) == no_other_refs() |
| 1171 | + assert gc.get_referrers(foo) == no_other_refs() |
| 1172 | + assert gc.get_referrers(arg) == no_other_refs() |
| 1173 | + assert gc.get_referrers(v) == no_other_refs() |
| 1174 | + |
| 1175 | + |
| 1176 | +@pytest.mark.skipif( |
| 1177 | + sys.implementation.name == "pypy", |
| 1178 | + reason=( |
| 1179 | + "gc.get_referrers is broken on PyPy (see " |
| 1180 | + "https://github.com/pypy/pypy/issues/5075)" |
| 1181 | + ), |
| 1182 | +) |
| 1183 | +async def test_run_sync_workerreferences_exc() -> None: |
| 1184 | + |
| 1185 | + class MyException(Exception): |
| 1186 | + pass |
| 1187 | + |
| 1188 | + def throw() -> None: |
| 1189 | + raise MyException |
| 1190 | + |
| 1191 | + e = None |
| 1192 | + try: |
| 1193 | + await to_thread_run_sync(throw) |
| 1194 | + except MyException as err: |
| 1195 | + e = err |
| 1196 | + |
| 1197 | + gc_collect_harder() |
| 1198 | + |
| 1199 | + assert gc.get_referrers(e) == no_other_refs() |
0 commit comments