summaryrefslogtreecommitdiffstats
path: root/Lib/test/support/__init__.py
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2017-09-14 20:07:24 (GMT)
committerGitHub <noreply@github.com>2017-09-14 20:07:24 (GMT)
commitff40ecda73178dfcad24e26240d684356ef20793 (patch)
tree0533f9354f838a97a67fc22749b1b1b7374691fb /Lib/test/support/__init__.py
parentb8c7be2c523b012e57915182543d06657161057f (diff)
downloadcpython-ff40ecda73178dfcad24e26240d684356ef20793.zip
cpython-ff40ecda73178dfcad24e26240d684356ef20793.tar.gz
cpython-ff40ecda73178dfcad24e26240d684356ef20793.tar.bz2
bpo-31234: Add test.support.wait_threads_exit() (#3578)
Use _thread.count() to wait until threads exit. The new context manager prevents the "dangling thread" warning.
Diffstat (limited to 'Lib/test/support/__init__.py')
-rw-r--r--Lib/test/support/__init__.py35
1 files changed, 35 insertions, 0 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index df23505..63f7a91 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2072,6 +2072,41 @@ def reap_threads(func):
return decorator
+@contextlib.contextmanager
+def wait_threads_exit(timeout=60.0):
+ """
+ bpo-31234: Context manager to wait until all threads created in the with
+ statement exit.
+
+ Use _thread.count() to check if threads exited. Indirectly, wait until
+ threads exit the internal t_bootstrap() C function of the _thread module.
+
+ threading_setup() and threading_cleanup() are designed to emit a warning
+ if a test leaves running threads in the background. This context manager
+ is designed to cleanup threads started by the _thread.start_new_thread()
+ which doesn't allow to wait for thread exit, whereas thread.Thread has a
+ join() method.
+ """
+ old_count = _thread._count()
+ try:
+ yield
+ finally:
+ start_time = time.monotonic()
+ deadline = start_time + timeout
+ while True:
+ count = _thread._count()
+ if count <= old_count:
+ break
+ if time.monotonic() > deadline:
+ dt = time.monotonic() - start_time
+ msg = (f"wait_threads() failed to cleanup {count - old_count} "
+ f"threads after {dt:.1f} seconds "
+ f"(count: {count}, old count: {old_count})")
+ raise AssertionError(msg)
+ time.sleep(0.010)
+ gc_collect()
+
+
def reap_children():
"""Use this function at the end of test_main() whenever sub-processes
are started. This will help ensure that no extra children (zombies)