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
This commit is contained in:
parent
17657bb945
commit
ee84a60858
@ -17,6 +17,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import signal
|
import signal
|
||||||
import itertools
|
import itertools
|
||||||
|
import threading
|
||||||
from _weakrefset import WeakSet
|
from _weakrefset import WeakSet
|
||||||
|
|
||||||
#
|
#
|
||||||
@ -311,6 +312,7 @@ class BaseProcess(object):
|
|||||||
sys.stderr.write('Process %s:\n' % self.name)
|
sys.stderr.write('Process %s:\n' % self.name)
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
finally:
|
finally:
|
||||||
|
threading._shutdown()
|
||||||
util.info('process exiting with exitcode %d' % exitcode)
|
util.info('process exiting with exitcode %d' % exitcode)
|
||||||
sys.stdout.flush()
|
sys.stdout.flush()
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
@ -542,6 +542,32 @@ class _TestProcess(BaseTestCase):
|
|||||||
p.join()
|
p.join()
|
||||||
close_queue(q)
|
close_queue(q)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _test_wait_for_threads(self, evt):
|
||||||
|
def func1():
|
||||||
|
time.sleep(0.5)
|
||||||
|
evt.set()
|
||||||
|
|
||||||
|
def func2():
|
||||||
|
time.sleep(20)
|
||||||
|
evt.clear()
|
||||||
|
|
||||||
|
threading.Thread(target=func1).start()
|
||||||
|
threading.Thread(target=func2, daemon=True).start()
|
||||||
|
|
||||||
|
def test_wait_for_threads(self):
|
||||||
|
# A child process should wait for non-daemonic threads to end
|
||||||
|
# before exiting
|
||||||
|
if self.TYPE == 'threads':
|
||||||
|
self.skipTest('test not appropriate for {}'.format(self.TYPE))
|
||||||
|
|
||||||
|
evt = self.Event()
|
||||||
|
proc = self.Process(target=self._test_wait_for_threads, args=(evt,))
|
||||||
|
proc.start()
|
||||||
|
proc.join()
|
||||||
|
self.assertTrue(evt.is_set())
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
@ -1284,6 +1284,9 @@ def _shutdown():
|
|||||||
# the main thread's tstate_lock - that won't happen until the interpreter
|
# the main thread's tstate_lock - that won't happen until the interpreter
|
||||||
# is nearly dead. So we release it here. Note that just calling _stop()
|
# is nearly dead. So we release it here. Note that just calling _stop()
|
||||||
# isn't enough: other threads may already be waiting on _tstate_lock.
|
# isn't enough: other threads may already be waiting on _tstate_lock.
|
||||||
|
if _main_thread._is_stopped:
|
||||||
|
# _shutdown() was already called
|
||||||
|
return
|
||||||
tlock = _main_thread._tstate_lock
|
tlock = _main_thread._tstate_lock
|
||||||
# The main thread isn't finished yet, so its thread state lock can't have
|
# The main thread isn't finished yet, so its thread state lock can't have
|
||||||
# been released.
|
# been released.
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
Non-daemonic threads created by a multiprocessing.Process are now joined on
|
||||||
|
child exit.
|
Loading…
x
Reference in New Issue
Block a user