]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.12] gh-88887: Cleanup `multiprocessing.resource_tracker.ResourceTracker` upon...
authorluccabb <32229669+luccabb@users.noreply.github.com>
Fri, 21 Mar 2025 10:15:35 +0000 (03:15 -0700)
committerGitHub <noreply@github.com>
Fri, 21 Mar 2025 10:15:35 +0000 (11:15 +0100)
Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
(cherry picked from commit f53e7de6a84a0f535efb75c3671283b801a1af0f)

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 23fea295c357a586e063553ff2c9d23abb92915d..e4da1293033412dc17a8ce4cfc45ce41a6179e89 100644 (file)
@@ -70,22 +70,46 @@ 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
 
-            os.waitpid(self._pid, 0)
-            self._pid = None
+        waitpid(self._pid, 0)
+        self._pid = 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.