Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 7c7ed49

Browse files
miss-islingtonDrinoserhiy-storchaka
authored
[3.12] gh-88110: Clear concurrent.futures.thread._threads_queues after fork to avoid joining parent process' threads (GH-126098) (GH-127164)
Threads are gone after fork, so clear the queues too. Otherwise the child process (here created via multiprocessing.Process) crashes on interpreter exit. (cherry picked from commit 1848ce6) Co-authored-by: Andrei Bodrov <[email protected]> Co-authored-by: Serhiy Storchaka <[email protected]>
1 parent e7ab978 commit 7c7ed49

File tree

3 files changed

+22
-0
lines changed

3 files changed

+22
-0
lines changed

Lib/concurrent/futures/thread.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def _python_exit():
4141
os.register_at_fork(before=_global_shutdown_lock.acquire,
4242
after_in_child=_global_shutdown_lock._at_fork_reinit,
4343
after_in_parent=_global_shutdown_lock.release)
44+
os.register_at_fork(after_in_child=_threads_queues.clear)
4445

4546

4647
class _WorkItem:

Lib/test/test_concurrent_futures/test_thread_pool.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ def submit(pool):
6464
with futures.ProcessPoolExecutor(1, mp_context=mp.get_context('fork')) as workers:
6565
workers.submit(tuple)
6666

67+
@support.requires_fork()
68+
@unittest.skipUnless(hasattr(os, 'register_at_fork'), 'need os.register_at_fork')
69+
def test_process_fork_from_a_threadpool(self):
70+
# bpo-43944: clear concurrent.futures.thread._threads_queues after fork,
71+
# otherwise child process will try to join parent thread
72+
def fork_process_and_return_exitcode():
73+
# Ignore the warning about fork with threads.
74+
with self.assertWarnsRegex(DeprecationWarning,
75+
r"use of fork\(\) may lead to deadlocks in the child"):
76+
p = mp.get_context('fork').Process(target=lambda: 1)
77+
p.start()
78+
p.join()
79+
return p.exitcode
80+
81+
with futures.ThreadPoolExecutor(1) as pool:
82+
process_exitcode = pool.submit(fork_process_and_return_exitcode).result()
83+
84+
self.assertEqual(process_exitcode, 0)
85+
6786
def test_executor_map_current_future_cancel(self):
6887
stop_event = threading.Event()
6988
log = []
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed :class:`multiprocessing.Process` reporting a ``.exitcode`` of 1 even on success when
2+
using the ``"fork"`` start method while using a :class:`concurrent.futures.ThreadPoolExecutor`.

0 commit comments

Comments
 (0)