https://github.com/python/cpython/commit/f53e7de6a84a0f535efb75c3671283b801a1af0f commit: f53e7de6a84a0f535efb75c3671283b801a1af0f branch: main author: luccabb <32229669+lucc...@users.noreply.github.com> committer: vstinner <vstin...@python.org> date: 2025-03-20T17:44:37+01:00 summary:
gh-88887: Cleanup `multiprocessing.resource_tracker.ResourceTracker` upon deletion (#130429) Co-authored-by: Victor Stinner <vstin...@python.org> Co-authored-by: Gregory P. Smith <g...@krypto.org> files: A Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-14-47-46.gh-issue-88887.V3U0CV.rst M Lib/multiprocessing/resource_tracker.py diff --git a/Lib/multiprocessing/resource_tracker.py b/Lib/multiprocessing/resource_tracker.py index 90e036ae905afa..05633ac21a259c 100644 --- a/Lib/multiprocessing/resource_tracker.py +++ b/Lib/multiprocessing/resource_tracker.py @@ -75,29 +75,53 @@ def _reentrant_call_error(self): 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 index 00000000000000..1a6c9483e2a35e --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-02-21-14-47-46.gh-issue-88887.V3U0CV.rst @@ -0,0 +1 @@ +Fixing multiprocessing Resource Tracker process leaking, usually observed when running Python as PID 1. _______________________________________________ Python-checkins mailing list -- python-checkins@python.org To unsubscribe send an email to python-checkins-le...@python.org https://mail.python.org/mailman3/lists/python-checkins.python.org/ Member address: arch...@mail-archive.com