]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-127586: properly restore blocked signals in resource_tracker.py (GH-127587)
authorStephen Hansen <stephen.paul.hansen@gmail.com>
Sun, 15 Dec 2024 19:53:22 +0000 (14:53 -0500)
committerGitHub <noreply@github.com>
Sun, 15 Dec 2024 19:53:22 +0000 (11:53 -0800)
* 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>
Lib/multiprocessing/resource_tracker.py
Lib/test/_test_multiprocessing.py
Misc/NEWS.d/next/Library/2024-12-03-20-28-08.gh-issue-127586.zgotYF.rst [new file with mode: 0644]

index 20ddd9c50e3d8849b2b106ee6d5b01f81ffbc218..90e036ae905afa8c3fd008a5dd325fd79efacee7 100644 (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
index 80b08b8ac66899fe37fb339267ca4927e2f49ac3..01e92f0d8d6b301e40c683eb378d28592f3efd81 100644 (file)
@@ -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 (file)
index 0000000..80217bd
--- /dev/null
@@ -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*.