Skip to content

Commit aeb9b65

Browse files
stephen-hansenZeroIntensitygpshead
authored
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 <[email protected]> Co-authored-by: Gregory P. Smith <[email protected]>
1 parent 64173cd commit aeb9b65

File tree

3 files changed

+28
-3
lines changed

3 files changed

+28
-3
lines changed

Lib/multiprocessing/resource_tracker.py

+4-3
Original file line numberDiff line numberDiff line change
@@ -155,13 +155,14 @@ def ensure_running(self):
155155
# that can make the child die before it registers signal handlers
156156
# for SIGINT and SIGTERM. The mask is unregistered after spawning
157157
# the child.
158+
prev_sigmask = None
158159
try:
159160
if _HAVE_SIGMASK:
160-
signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
161+
prev_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, _IGNORED_SIGNALS)
161162
pid = util.spawnv_passfds(exe, args, fds_to_pass)
162163
finally:
163-
if _HAVE_SIGMASK:
164-
signal.pthread_sigmask(signal.SIG_UNBLOCK, _IGNORED_SIGNALS)
164+
if prev_sigmask is not None:
165+
signal.pthread_sigmask(signal.SIG_SETMASK, prev_sigmask)
165166
except:
166167
os.close(w)
167168
raise

Lib/test/_test_multiprocessing.py

+21
Original file line numberDiff line numberDiff line change
@@ -6045,6 +6045,27 @@ def test_resource_tracker_exit_code(self):
60456045
cleanup=cleanup,
60466046
)
60476047

6048+
@unittest.skipUnless(hasattr(signal, "pthread_sigmask"), "pthread_sigmask is not available")
6049+
def test_resource_tracker_blocked_signals(self):
6050+
#
6051+
# gh-127586: Check that resource_tracker does not override blocked signals of caller.
6052+
#
6053+
from multiprocessing.resource_tracker import ResourceTracker
6054+
orig_sigmask = signal.pthread_sigmask(signal.SIG_BLOCK, set())
6055+
signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1}
6056+
6057+
try:
6058+
for sig in signals:
6059+
signal.pthread_sigmask(signal.SIG_SETMASK, {sig})
6060+
self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
6061+
tracker = ResourceTracker()
6062+
tracker.ensure_running()
6063+
self.assertEqual(signal.pthread_sigmask(signal.SIG_BLOCK, set()), {sig})
6064+
tracker._stop()
6065+
finally:
6066+
# restore sigmask to what it was before executing test
6067+
signal.pthread_sigmask(signal.SIG_SETMASK, orig_sigmask)
6068+
60486069
class TestSimpleQueue(unittest.TestCase):
60496070

60506071
@classmethod
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:class:`multiprocessing.pool.Pool` now properly restores blocked signal handlers
2+
of the parent thread when creating processes via either *spawn* or
3+
*forkserver*.

0 commit comments

Comments
 (0)