|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
| 3 | +import os |
3 | 4 | import threading
|
4 | 5 | import time
|
5 | 6 | from contextlib import contextmanager
|
@@ -195,3 +196,47 @@ def deliver(_: object) -> NoReturn:
|
195 | 196 | err = capfd.readouterr().err
|
196 | 197 | assert "don't do this" in err
|
197 | 198 | assert "delivering result" in err
|
| 199 | + |
| 200 | + |
| 201 | +@pytest.mark.skipif(not hasattr(os, "fork"), reason="os.fork isn't supported") |
| 202 | +def test_clear_thread_cache_after_fork() -> None: |
| 203 | + assert hasattr(os, "fork") |
| 204 | + |
| 205 | + def foo() -> None: |
| 206 | + pass |
| 207 | + |
| 208 | + # ensure the thread cache exists |
| 209 | + done = threading.Event() |
| 210 | + start_thread_soon(foo, lambda _: done.set()) |
| 211 | + done.wait() |
| 212 | + |
| 213 | + child_pid = os.fork() |
| 214 | + |
| 215 | + # try using it |
| 216 | + done = threading.Event() |
| 217 | + start_thread_soon(foo, lambda _: done.set()) |
| 218 | + done.wait() |
| 219 | + |
| 220 | + if child_pid != 0: |
| 221 | + # if this test fails, this will hang, triggering a timeout. |
| 222 | + os.waitpid(child_pid, 0) |
| 223 | + else: |
| 224 | + # this is necessary because os._exit doesn't unwind the stack, |
| 225 | + # so coverage doesn't get to automatically stop and save |
| 226 | + # coverage information. |
| 227 | + try: |
| 228 | + import coverage |
| 229 | + |
| 230 | + cov = coverage.Coverage.current() |
| 231 | + # the following pragmas are necessary because if coverage: |
| 232 | + # - isn't running, then it can't record the branch not |
| 233 | + # taken |
| 234 | + # - isn't installed, then it can't record the ImportError |
| 235 | + |
| 236 | + if cov: # pragma: no branch |
| 237 | + cov.stop() |
| 238 | + cov.save() |
| 239 | + except ImportError: # pragma: no cover |
| 240 | + pass |
| 241 | + |
| 242 | + os._exit(0) # pragma: no cover # coverage was stopped above. |
0 commit comments