[3.13] gh-127586: multiprocessing.Pool does not properly restore blocked signals (try 2) (GH-128011) (#128298)

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.
(cherry picked from commit aeb9b65aa2)

Co-authored-by: Stephen Hansen <stephen.paul.hansen@gmail.com>
Co-authored-by: Peter Bierma <zintensitydev@gmail.com>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
Miss Islington (bot) 2024-12-29 20:02:53 +01:00 committed by GitHub
parent aea2e03b27
commit 16fde763e5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 28 additions and 3 deletions

View file

@ -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

View file

@ -5950,6 +5950,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

View file

@ -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*.