]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-88887: Cleanup `multiprocessing.resource_tracker.ResourceTracker` upon...
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Thu, 20 Mar 2025 17:08:58 +0000 (18:08 +0100)
committerGitHub <noreply@github.com>
Thu, 20 Mar 2025 17:08:58 +0000 (17:08 +0000)
gh-88887: Cleanup `multiprocessing.resource_tracker.ResourceTracker` upon deletion (GH-130429)
(cherry picked from commit f53e7de6a84a0f535efb75c3671283b801a1af0f)

Co-authored-by: luccabb <32229669+luccabb@users.noreply.github.com>
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
Lib/multiprocessing/resource_tracker.py
Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-14-47-46.gh-issue-88887.V3U0CV.rst [new file with mode: 0644]

index 90e036ae905afa8c3fd008a5dd325fd79efacee7..05633ac21a259cf9b94a8616786c1b6329a74654 100644 (file)
@@ -75,29 +75,53 @@ class ResourceTracker(object):
         raise ReentrantCallError(
             "Reentrant call into the multiprocessing resource tracker")
 
-    def _stop(self):
-        with self._lock:
-            # This should not happen (_stop() isn't called by a finalizer)
-            # but we check for it anyway.
-            if self._lock._recursion_count() > 1:
-                return self._reentrant_call_error()
-            if self._fd is None:
-                # not running
-                return
-
-            # closing the "alive" file descriptor stops main()
-            os.close(self._fd)
-            self._fd = None
+    def __del__(self):
+        # making sure child processess are cleaned before ResourceTracker
+        # gets destructed.
+        # see https://github.com/python/cpython/issues/88887
+        self._stop(use_blocking_lock=False)
+
+    def _stop(self, use_blocking_lock=True):
+        if use_blocking_lock:
+            with self._lock:
+                self._stop_locked()
+        else:
+            acquired = self._lock.acquire(blocking=False)
+            try:
+                self._stop_locked()
+            finally:
+                if acquired:
+                    self._lock.release()
+
+    def _stop_locked(
+        self,
+        close=os.close,
+        waitpid=os.waitpid,
+        waitstatus_to_exitcode=os.waitstatus_to_exitcode,
+    ):
+        # This shouldn't happen (it might when called by a finalizer)
+        # so we check for it anyway.
+        if self._lock._recursion_count() > 1:
+            return self._reentrant_call_error()
+        if self._fd is None:
+            # not running
+            return
+        if self._pid is None:
+            return
+
+        # closing the "alive" file descriptor stops main()
+        close(self._fd)
+        self._fd = None
 
-            _, status = os.waitpid(self._pid, 0)
+        _, status = waitpid(self._pid, 0)
 
-            self._pid = None
+        self._pid = None
 
-            try:
-                self._exitcode = os.waitstatus_to_exitcode(status)
-            except ValueError:
-                # os.waitstatus_to_exitcode may raise an exception for invalid values
-                self._exitcode = None
+        try:
+            self._exitcode = waitstatus_to_exitcode(status)
+        except ValueError:
+            # os.waitstatus_to_exitcode may raise an exception for invalid values
+            self._exitcode = None
 
     def getfd(self):
         self.ensure_running()
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-14-47-46.gh-issue-88887.V3U0CV.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-14-47-46.gh-issue-88887.V3U0CV.rst
new file mode 100644 (file)
index 0000000..1a6c948
--- /dev/null
@@ -0,0 +1 @@
+Fixing multiprocessing Resource Tracker process leaking, usually observed when running Python as PID 1.