summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStephen Hansen <stephen.paul.hansen@gmail.com>2024-12-15 19:53:22 (GMT)
committerGitHub <noreply@github.com>2024-12-15 19:53:22 (GMT)
commit46006a1b355f75d06c10e7b8086912c483b34487 (patch)
tree03efaaf9e69c71002d4babf7e76e028a5a406f80
parent7b8bd3b2b81f4aca63c5b603b56998f6b3ee2611 (diff)
downloadcpython-46006a1b355f75d06c10e7b8086912c483b34487.zip
cpython-46006a1b355f75d06c10e7b8086912c483b34487.tar.gz
cpython-46006a1b355f75d06c10e7b8086912c483b34487.tar.bz2
gh-127586: properly restore blocked signals in resource_tracker.py (GH-127587)
* 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. * Adding resource_tracker blocked signals test Co-authored-by: Peter Bierma <zintensitydev@gmail.com> Co-authored-by: Gregory P. Smith <greg@krypto.org>
-rw-r--r--Lib/multiprocessing/resource_tracker.py7
-rw-r--r--Lib/test/_test_multiprocessing.py15
-rw-r--r--Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst3
3 files changed, 22 insertions, 3 deletions
diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py
index 20ddd9c..90e036a 100644
--- a/Lib/multiprocessing/resource_tracker.py
+++ b/Lib/multiprocessing/resource_tracker.py
@@ -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
diff --git a/Lib/test/_test_multiprocessing.py b/Lib/test/_test_multiprocessing.py
index 80b08b8..01e92f0 100644
--- a/Lib/test/_test_multiprocessing.py
+++ b/Lib/test/_test_multiprocessing.py
@@ -6044,6 +6044,21 @@ class TestResourceTracker(unittest.TestCase):
self._test_resource_tracker_leak_resources(
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
+ signals = {signal.SIGTERM, signal.SIGINT, signal.SIGUSR1}
+
+ 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()
class TestSimpleQueue(unittest.TestCase):
diff --git a/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst
new file mode 100644
index 0000000..80217bd
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst
@@ -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*.