summaryrefslogtreecommitdiffstats
path: root/Lib/test/support
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@redhat.com>2018-06-01 13:51:02 (GMT)
committerGitHub <noreply@github.com>2018-06-01 13:51:02 (GMT)
commit5dbb48aaac0ff74648b355ebdde222856004b1ef (patch)
treec47bd93d6333eb31b1461d75c5463ef058d666b2 /Lib/test/support
parent95681c7a7ddd436ba7d6c10d1202c33dd6bd648b (diff)
downloadcpython-5dbb48aaac0ff74648b355ebdde222856004b1ef.zip
cpython-5dbb48aaac0ff74648b355ebdde222856004b1ef.tar.gz
cpython-5dbb48aaac0ff74648b355ebdde222856004b1ef.tar.bz2
[3.6] bpo-31234: Add test.support.wait_threads_exit() (GH-3578) (GH-7315)
* bpo-31234: Add test.support.wait_threads_exit() (GH-3578) Use _thread.count() to wait until threads exit. The new context manager prevents the "dangling thread" warning. (cherry picked from commit ff40ecda73178dfcad24e26240d684356ef20793) * bpo-31234: Try to fix lock_tests warning (#3557) Try to fix the "Warning -- threading_cleanup() failed to cleanup 1 threads" warning in test.lock_tests: wait a little bit longer to give time to the threads to complete. Warning seen on test_thread and test_importlib. (cherry picked from commit 096ae3373abac2c8b3a26a3fe33cc8bd4cbccd4e)
Diffstat (limited to 'Lib/test/support')
-rw-r--r--Lib/test/support/__init__.py36
1 files changed, 36 insertions, 0 deletions
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 3def27b..8f1aeee 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -2112,6 +2112,42 @@ def reap_threads(func):
threading_cleanup(*key)
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)