aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/multiprocessing/popen_spawn_win32.py
diff options
context:
space:
mode:
authorVictor Stinner <vstinner@python.org>2023-12-15 15:57:49 +0100
committerGitHub <noreply@github.com>2023-12-15 15:57:49 +0100
commit4026ad5b2c595b855a3605420cfa0e3d49e63db7 (patch)
tree2698e0d343a0e7488ee397ce93aff6284cc5edd9 /Lib/multiprocessing/popen_spawn_win32.py
parentd1a2adfb0820ee730fa3e4bbc4bd88a67aa50666 (diff)
downloadcpython-4026ad5b2c595b855a3605420cfa0e3d49e63db7.tar.gz
cpython-4026ad5b2c595b855a3605420cfa0e3d49e63db7.zip
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.
Diffstat (limited to 'Lib/multiprocessing/popen_spawn_win32.py')
-rw-r--r--Lib/multiprocessing/popen_spawn_win32.py54
1 files changed, 30 insertions, 24 deletions
diff --git a/Lib/multiprocessing/popen_spawn_win32.py b/Lib/multiprocessing/popen_spawn_win32.py
index af044305709..49d4c7eea22 100644
--- a/Lib/multiprocessing/popen_spawn_win32.py
+++ b/Lib/multiprocessing/popen_spawn_win32.py
@@ -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))
-
- 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 self.returncode is not None:
+ return self.returncode
+
+ 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