]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111644: Fix support threading_cleanup() (#111714)
authorVictor Stinner <vstinner@python.org>
Sat, 4 Nov 2023 00:18:42 +0000 (01:18 +0100)
committerGitHub <noreply@github.com>
Sat, 4 Nov 2023 00:18:42 +0000 (00:18 +0000)
Copy the list of dangling threads to make sure that the list of
"Dangling thread" is complete. Previously, the list was incomplete if
threads completed just before the list was displayed.

Changes:

* Rewrite the warning to make it easier to understand.
* Use support.sleeping_retry().
* threading_cleanup() no longer copies threading._dangling,
  but only counts the number of dangling thread.
* Remove support.gc_support() call.

Lib/test/support/threading_helper.py

index 7f16050f32b9d192de2c740f3107b86332eb2b2c..afa25a76f63829555028aba818a7daa39c9b5d47 100644 (file)
@@ -22,34 +22,37 @@ from test import support
 
 
 def threading_setup():
-    return _thread._count(), threading._dangling.copy()
+    return _thread._count(), len(threading._dangling)
 
 
 def threading_cleanup(*original_values):
-    _MAX_COUNT = 100
-
-    for count in range(_MAX_COUNT):
-        values = _thread._count(), threading._dangling
-        if values == original_values:
-            break
-
-        if not count:
-            # Display a warning at the first iteration
-            support.environment_altered = True
-            dangling_threads = values[1]
-            support.print_warning(f"threading_cleanup() failed to cleanup "
-                                  f"{values[0] - original_values[0]} threads "
-                                  f"(count: {values[0]}, "
-                                  f"dangling: {len(dangling_threads)})")
-            for thread in dangling_threads:
-                support.print_warning(f"Dangling thread: {thread!r}")
-
-            # Don't hold references to threads
-            dangling_threads = None
-        values = None
-
-        time.sleep(0.01)
-        support.gc_collect()
+    orig_count, orig_ndangling = original_values
+
+    timeout = 1.0
+    for _ in support.sleeping_retry(timeout, error=False):
+        # Copy the thread list to get a consistent output. threading._dangling
+        # is a WeakSet, its value changes when it's read.
+        dangling_threads = list(threading._dangling)
+        count = _thread._count()
+
+        if count <= orig_count:
+            return
+
+    # Timeout!
+    support.environment_altered = True
+    support.print_warning(
+        f"threading_cleanup() failed to clean up threads "
+        f"in {timeout:.1f} seconds\n"
+        f"  before: thread count={orig_count}, dangling={orig_ndangling}\n"
+        f"  after: thread count={count}, dangling={len(dangling_threads)}")
+    for thread in dangling_threads:
+        support.print_warning(f"Dangling thread: {thread!r}")
+
+    # The warning happens when a test spawns threads and some of these threads
+    # are still running after the test completes. To fix this warning, join
+    # threads explicitly to wait until they complete.
+    #
+    # To make the warning more likely, reduce the timeout.
 
 
 def reap_threads(func):