summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_threading.py
diff options
context:
space:
mode:
authorAntoine Pitrou <antoine@python.org>2023-11-04 13:59:24 (GMT)
committerGitHub <noreply@github.com>2023-11-04 13:59:24 (GMT)
commit0e9c364f4ac18a2237bdbac702b96bcf8ef9cb09 (patch)
tree8febb8282c2c1ebd73a18205ec5b9229a99ac4fe /Lib/test/test_threading.py
parenta28a3967ab9a189122f895d51d2551f7b3a273b0 (diff)
downloadcpython-0e9c364f4ac18a2237bdbac702b96bcf8ef9cb09.zip
cpython-0e9c364f4ac18a2237bdbac702b96bcf8ef9cb09.tar.gz
cpython-0e9c364f4ac18a2237bdbac702b96bcf8ef9cb09.tar.bz2
GH-110829: Ensure Thread.join() joins the OS thread (#110848)
Joining a thread now ensures the underlying OS thread has exited. This is required for safer fork() in multi-threaded processes. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com>
Diffstat (limited to 'Lib/test/test_threading.py')
-rw-r--r--Lib/test/test_threading.py47
1 files changed, 44 insertions, 3 deletions
diff --git a/Lib/test/test_threading.py b/Lib/test/test_threading.py
index 00a6437..146e2db 100644
--- a/Lib/test/test_threading.py
+++ b/Lib/test/test_threading.py
@@ -376,8 +376,8 @@ class ThreadTests(BaseTestCase):
# Issue 7481: Failure to start thread should cleanup the limbo map.
def fail_new_thread(*args):
raise threading.ThreadError()
- _start_new_thread = threading._start_new_thread
- threading._start_new_thread = fail_new_thread
+ _start_joinable_thread = threading._start_joinable_thread
+ threading._start_joinable_thread = fail_new_thread
try:
t = threading.Thread(target=lambda: None)
self.assertRaises(threading.ThreadError, t.start)
@@ -385,7 +385,7 @@ class ThreadTests(BaseTestCase):
t in threading._limbo,
"Failed to cleanup _limbo map on failure of Thread.start().")
finally:
- threading._start_new_thread = _start_new_thread
+ threading._start_joinable_thread = _start_joinable_thread
def test_finalize_running_thread(self):
# Issue 1402: the PyGILState_Ensure / _Release functions may be called
@@ -482,6 +482,47 @@ class ThreadTests(BaseTestCase):
finally:
sys.setswitchinterval(old_interval)
+ def test_join_from_multiple_threads(self):
+ # Thread.join() should be thread-safe
+ errors = []
+
+ def worker():
+ time.sleep(0.005)
+
+ def joiner(thread):
+ try:
+ thread.join()
+ except Exception as e:
+ errors.append(e)
+
+ for N in range(2, 20):
+ threads = [threading.Thread(target=worker)]
+ for i in range(N):
+ threads.append(threading.Thread(target=joiner,
+ args=(threads[0],)))
+ for t in threads:
+ t.start()
+ time.sleep(0.01)
+ for t in threads:
+ t.join()
+ if errors:
+ raise errors[0]
+
+ def test_join_with_timeout(self):
+ lock = _thread.allocate_lock()
+ lock.acquire()
+
+ def worker():
+ lock.acquire()
+
+ thread = threading.Thread(target=worker)
+ thread.start()
+ thread.join(timeout=0.01)
+ assert thread.is_alive()
+ lock.release()
+ thread.join()
+ assert not thread.is_alive()
+
def test_no_refcycle_through_target(self):
class RunSelfFunction(object):
def __init__(self, should_raise):