gh-88887: Cleanup multiprocessing.resource_tracker.ResourceTracker upon deletion (#130429)

Co-authored-by: Victor Stinner <vstinner@python.org>
Co-authored-by: Gregory P. Smith <greg@krypto.org>
This commit is contained in:
luccabb 2025-03-20 09:44:37 -07:00 committed by GitHub
parent 00a9844888
commit f53e7de6a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 46 additions and 21 deletions

View 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
_, status = os.waitpid(self._pid, 0)
self._pid = 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._exitcode = os.waitstatus_to_exitcode(status)
except ValueError:
# os.waitstatus_to_exitcode may raise an exception for invalid values
self._exitcode = None
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 = waitpid(self._pid, 0)
self._pid = 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()

View file

@ -0,0 +1 @@
Fixing multiprocessing Resource Tracker process leaking, usually observed when running Python as PID 1.