Skip to content

Commit 02f8f78

Browse files
[3.12] gh-111644: Fix support threading_cleanup() (GH-111714) (#111716)
gh-111644: Fix support threading_cleanup() (GH-111714) 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. (cherry picked from commit f62c7cc) Co-authored-by: Victor Stinner <[email protected]>
1 parent 1a95ad6 commit 02f8f78

File tree

1 file changed

+28
-25
lines changed

1 file changed

+28
-25
lines changed

Lib/test/support/threading_helper.py

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,34 +22,37 @@
2222

2323

2424
def threading_setup():
25-
return _thread._count(), threading._dangling.copy()
25+
return _thread._count(), len(threading._dangling)
2626

2727

2828
def threading_cleanup(*original_values):
29-
_MAX_COUNT = 100
30-
31-
for count in range(_MAX_COUNT):
32-
values = _thread._count(), threading._dangling
33-
if values == original_values:
34-
break
35-
36-
if not count:
37-
# Display a warning at the first iteration
38-
support.environment_altered = True
39-
dangling_threads = values[1]
40-
support.print_warning(f"threading_cleanup() failed to cleanup "
41-
f"{values[0] - original_values[0]} threads "
42-
f"(count: {values[0]}, "
43-
f"dangling: {len(dangling_threads)})")
44-
for thread in dangling_threads:
45-
support.print_warning(f"Dangling thread: {thread!r}")
46-
47-
# Don't hold references to threads
48-
dangling_threads = None
49-
values = None
50-
51-
time.sleep(0.01)
52-
support.gc_collect()
29+
orig_count, orig_ndangling = original_values
30+
31+
timeout = 1.0
32+
for _ in support.sleeping_retry(timeout, error=False):
33+
# Copy the thread list to get a consistent output. threading._dangling
34+
# is a WeakSet, its value changes when it's read.
35+
dangling_threads = list(threading._dangling)
36+
count = _thread._count()
37+
38+
if count <= orig_count:
39+
return
40+
41+
# Timeout!
42+
support.environment_altered = True
43+
support.print_warning(
44+
f"threading_cleanup() failed to clean up threads "
45+
f"in {timeout:.1f} seconds\n"
46+
f" before: thread count={orig_count}, dangling={orig_ndangling}\n"
47+
f" after: thread count={count}, dangling={len(dangling_threads)}")
48+
for thread in dangling_threads:
49+
support.print_warning(f"Dangling thread: {thread!r}")
50+
51+
# The warning happens when a test spawns threads and some of these threads
52+
# are still running after the test completes. To fix this warning, join
53+
# threads explicitly to wait until they complete.
54+
#
55+
# To make the warning more likely, reduce the timeout.
5356

5457

5558
def reap_threads(func):

0 commit comments

Comments
 (0)