|
4 | 4 | import sys |
5 | 5 | import threading |
6 | 6 | import time |
| 7 | +import unittest |
7 | 8 |
|
8 | 9 | from test import support |
9 | 10 |
|
@@ -87,19 +88,17 @@ def wait_threads_exit(timeout=None): |
87 | 88 | yield |
88 | 89 | finally: |
89 | 90 | start_time = time.monotonic() |
90 | | - deadline = start_time + timeout |
91 | | - while True: |
| 91 | + for _ in support.sleeping_retry(timeout, error=False): |
| 92 | + support.gc_collect() |
92 | 93 | count = _thread._count() |
93 | 94 | if count <= old_count: |
94 | 95 | break |
95 | | - if time.monotonic() > deadline: |
96 | | - dt = time.monotonic() - start_time |
97 | | - msg = (f"wait_threads() failed to cleanup {count - old_count} " |
98 | | - f"threads after {dt:.1f} seconds " |
99 | | - f"(count: {count}, old count: {old_count})") |
100 | | - raise AssertionError(msg) |
101 | | - time.sleep(0.010) |
102 | | - support.gc_collect() |
| 96 | + else: |
| 97 | + dt = time.monotonic() - start_time |
| 98 | + msg = (f"wait_threads() failed to cleanup {count - old_count} " |
| 99 | + f"threads after {dt:.1f} seconds " |
| 100 | + f"(count: {count}, old count: {old_count})") |
| 101 | + raise AssertionError(msg) |
103 | 102 |
|
104 | 103 |
|
105 | 104 | def join_thread(thread, timeout=None): |
@@ -207,3 +206,37 @@ def __exit__(self, *exc_info): |
207 | 206 | del self.exc_value |
208 | 207 | del self.exc_traceback |
209 | 208 | del self.thread |
| 209 | + |
| 210 | + |
| 211 | +def _can_start_thread() -> bool: |
| 212 | + """Detect whether Python can start new threads. |
| 213 | +
|
| 214 | + Some WebAssembly platforms do not provide a working pthread |
| 215 | + implementation. Thread support is stubbed and any attempt |
| 216 | + to create a new thread fails. |
| 217 | +
|
| 218 | + - wasm32-wasi does not have threading. |
| 219 | + - wasm32-emscripten can be compiled with or without pthread |
| 220 | + support (-s USE_PTHREADS / __EMSCRIPTEN_PTHREADS__). |
| 221 | + """ |
| 222 | + if sys.platform == "emscripten": |
| 223 | + return sys._emscripten_info.pthreads |
| 224 | + elif sys.platform == "wasi": |
| 225 | + return False |
| 226 | + else: |
| 227 | + # assume all other platforms have working thread support. |
| 228 | + return True |
| 229 | + |
| 230 | +can_start_thread = _can_start_thread() |
| 231 | + |
| 232 | +def requires_working_threading(*, module=False): |
| 233 | + """Skip tests or modules that require working threading. |
| 234 | +
|
| 235 | + Can be used as a function/class decorator or to skip an entire module. |
| 236 | + """ |
| 237 | + msg = "requires threading support" |
| 238 | + if module: |
| 239 | + if not can_start_thread: |
| 240 | + raise unittest.SkipTest(msg) |
| 241 | + else: |
| 242 | + return unittest.skipUnless(can_start_thread, msg) |
0 commit comments