8000 bpo-18966: non-daemonic threads created by a multiprocessing.Process … · python/cpython@ee84a60 · GitHub
[go: up one dir, main page]

Skip to content

Commit ee84a60

Browse files
authored
bpo-18966: non-daemonic threads created by a multiprocessing.Process should be joined on exit (#3111)
* bpo-18966: non-daemonic threads created by a multiprocessing.Process should be joined on exit * Add NEWS blurb
1 parent 17657bb commit ee84a60

File tree

4 files changed

+33
-0
lines changed

4 files changed

+33
-0
lines changed

Lib/multiprocessing/process.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
import sys
1818
import signal
1919
import itertools
20+
import threading
2021
from _weakrefset import WeakSet
2122

2223
#
@@ -311,6 +312,7 @@ def _bootstrap(self):
311312
sys.stderr.write('Process %s:\n' % self.name)
312313
traceback.print_exc()
313314
finally:
315+
threading._shutdown()
314316
util.info('process exiting with exitcode %d' % exitcode)
315317
sys.stdout.flush()
316318
sys.stderr.flush()

Lib/test/_test_multiprocessing.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,32 @@ def test_child_fd_inflation(self):
542542
p.join()
543543
close_queue(q)
544544

545+
@classmethod
546+
def _test_wait_for_threads(self, evt):
547+
def func1():
548+
time.sleep(0.5)
549+
evt.set()
550+
551+
def func2():
552+
time.sleep(20)
553+
evt.clear()
554+
555+
threading.Thread(target=func1).start()
556+
threading.Thread(target=func2, daemon=True).start()
557+
558+
def test_wait_for_threads(self):
559+
# A child process should wait for non-daemonic threads to end
560+
# before exiting
561+
if self.TYPE == 'threads':
562+
self.skipTest('test not appropriate for {}'.format(self.TYPE))
563+
564+
evt = self.Event()
565+
proc = self.Process(target=self._test_wait_for_threads, args=(evt,))
566+
proc.start()
567+
proc.join()
568+
self.assertTrue(evt.is_set())
569+
570+
545571
#
546572
#
547573
#

Lib/threading.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1284,6 +1284,9 @@ def _shutdown():
12841284
# the main thread's tstate_lock - that won't happen until the interpreter
12851285
# is nearly dead. So we release it here. Note that just calling _stop()
12861286
# isn't enough: other threads may already be waiting on _tstate_lock.
1287+
if _main_thread._is_stopped:
1288+
# _shutdown() was already called
1289+
return
12871290
tlock = _main_thread._tstate_lock
12881291
# The main thread isn't finished yet, so its thread state lock can't have
12891292
# been released.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Non-daemonic threads created by a multiprocessing.Process are now joined on
2+
child exit.

0 commit comments

Comments
 (0)
0