gh-113009: Fix multiprocessing Process.terminate() on Windows (#113128)

On Windows, Process.terminate() no longer sets the returncode
attribute to always call WaitForSingleObject() in Process.wait().
Previously, sometimes the process was still running after
TerminateProcess() even if GetExitCodeProcess() is not STILL_ACTIVE.
This commit is contained in:
Victor Stinner 2023-12-15 15:57:49 +01:00 committed by GitHub
parent d1a2adfb08
commit 4026ad5b2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 34 additions and 23 deletions

View File

@ -101,18 +101,20 @@ class Popen(object):
return reduction.duplicate(handle, self.sentinel)
def wait(self, timeout=None):
if self.returncode is None:
if timeout is None:
msecs = _winapi.INFINITE
else:
msecs = max(0, int(timeout * 1000 + 0.5))
if self.returncode is not None:
return self.returncode
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
if res == _winapi.WAIT_OBJECT_0:
code = _winapi.GetExitCodeProcess(self._handle)
if code == TERMINATE:
code = -signal.SIGTERM
self.returncode = code
if timeout is None:
msecs = _winapi.INFINITE
else:
msecs = max(0, int(timeout * 1000 + 0.5))
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
if res == _winapi.WAIT_OBJECT_0:
code = _winapi.GetExitCodeProcess(self._handle)
if code == TERMINATE:
code = -signal.SIGTERM
self.returncode = code
return self.returncode
@ -120,18 +122,22 @@ class Popen(object):
return self.wait(timeout=0)
def terminate(self):
if self.returncode is None:
try:
_winapi.TerminateProcess(int(self._handle), TERMINATE)
except PermissionError:
# ERROR_ACCESS_DENIED (winerror 5) is received when the
# process already died.
code = _winapi.GetExitCodeProcess(int(self._handle))
if code == _winapi.STILL_ACTIVE:
raise
self.returncode = code
else:
self.returncode = -signal.SIGTERM
if self.returncode is not None:
return
try:
_winapi.TerminateProcess(int(self._handle), TERMINATE)
except PermissionError:
# ERROR_ACCESS_DENIED (winerror 5) is received when the
# process already died.
code = _winapi.GetExitCodeProcess(int(self._handle))
if code == _winapi.STILL_ACTIVE:
raise
# gh-113009: Don't set self.returncode. Even if GetExitCodeProcess()
# returns an exit code different than STILL_ACTIVE, the process can
# still be running. Only set self.returncode once WaitForSingleObject()
# returns WAIT_OBJECT_0 in wait().
kill = terminate

View File

@ -0,0 +1,5 @@
:mod:`multiprocessing`: On Windows, fix a race condition in
``Process.terminate()``: no longer set the ``returncode`` attribute to
always call ``WaitForSingleObject()`` in ``Process.wait()``. Previously,
sometimes the process was still running after ``TerminateProcess()`` even if
``GetExitCodeProcess()`` is not ``STILL_ACTIVE``. Patch by Victor Stinner.