mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-127586: multiprocessing.Pool does not properly restore blocked signals (try 2) (GH-128011)
Correct pthread_sigmask in resource_tracker to restore old signals Using SIG_UNBLOCK to remove blocked "ignored signals" may accidentally cause side effects if the calling parent already had said signals blocked to begin with and did not intend to unblock them when creating a pool. Use SIG_SETMASK instead with the previous mask of blocked signals to restore the original blocked set. Co-authored-by: Peter Bierma <zintensitydev@gmail.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
parent
64173cd6f2
commit
aeb9b65aa2
3 changed files with 28 additions and 3 deletions
|
@ -155,13 +155,14 @@ class ResourceTracker(object):
|
|||
# that can make the child die before it registers signal handlers
|
||||
# for SIGINT and SIGTERM. The mask is unregistered after spawning
|
||||
# the child.
|
||||
prev_sigmask = None
|
||||
try:
|
||||
if _HAVE_SIGMASK:
|
||||
signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
|
||||
prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
|
||||
pid = util.spawnv_passfds(exe, args, fds_to_pass)
|
||||
finally:
|
||||
if _HAVE_SIGMASK:
|
||||
signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
|
||||
if prev_sigmask is not None:
|
||||
signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
|
||||
except:
|
||||
os.close(w)
|
||||
raise
|
||||
|
|
|
@ -6045,6 +6045,27 @@ class TestResourceTracker(unittest.TestCase):
|
|||
cleanup=cleanup,
|
||||
)
|
||||
|
||||
@unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available")
|
||||
def test_resource_tracker_blocked_signals(self):
|
||||
#
|
||||
# gh-127586: Check that resource_tracker does not override blocked signals of caller.
|
||||
#
|
||||
from multiprocessing.resource_tracker import ResourceTracker
|
||||
orig_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, set())
|
||||
signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1}
|
||||
|
||||
try:
|
||||
for sig in signals:
|
||||
signal.pthread_sigmask(signal.SIG_SETMASK, {sig})
|
||||
self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
|
||||
tracker = ResourceTracker()
|
||||
tracker.ensure_running()
|
||||
self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
|
||||
tracker._stop()
|
||||
finally:
|
||||
# restore sigmask to what it was before executing test
|
||||
signal.pthread_sigmask(signal.SIG_SETMASK, orig_sigmask)
|
||||
|
||||
class TestSimpleQueue(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers
|
||||
of the parent thread when creating processes via either *spawn* or
|
||||
*forkserver*.
|
Loading…
Add table
Add a link
Reference in a new issue