Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 4026ad5

Browse filesBrowse files
authored
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.
1 parent d1a2adf commit 4026ad5
Copy full SHA for 4026ad5

File tree

Expand file treeCollapse file tree

2 files changed

+35
-24
lines changed
Filter options
Expand file treeCollapse file tree

2 files changed

+35
-24
lines changed

‎Lib/multiprocessing/popen_spawn_win32.py

Copy file name to clipboardExpand all lines: Lib/multiprocessing/popen_spawn_win32.py
+30-24Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -101,37 +101,43 @@ def duplicate_for_child(self, handle):
101101
return reduction.duplicate(handle, self.sentinel)
102102

103103
def wait(self, timeout=None):
104-
if self.returncode is None:
105-
if timeout is None:
106-
msecs = _winapi.INFINITE
107-
else:
108-
msecs = max(0, int(timeout * 1000 + 0.5))
109-
110-
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
111-
if res == _winapi.WAIT_OBJECT_0:
112-
code = _winapi.GetExitCodeProcess(self._handle)
113-
if code == TERMINATE:
114-
code = -signal.SIGTERM
115-
self.returncode = code
104+
if self.returncode is not None:
105+
return self.returncode
106+
107+
if timeout is None:
108+
msecs = _winapi.INFINITE
109+
else:
110+
msecs = max(0, int(timeout * 1000 + 0.5))
111+
112+
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
113+
if res == _winapi.WAIT_OBJECT_0:
114+
code = _winapi.GetExitCodeProcess(self._handle)
115+
if code == TERMINATE:
116+
code = -signal.SIGTERM
117+
self.returncode = code
116118

117119
return self.returncode
118120

119121
def poll(self):
120122
return self.wait(timeout=0)
121123

122124
def terminate(self):
123-
if self.returncode is None:
124-
try:
125-
_winapi.TerminateProcess(int(self._handle), TERMINATE)
126-
except PermissionError:
127-
# ERROR_ACCESS_DENIED (winerror 5) is received when the
128-
# process already died.
129-
code = _winapi.GetExitCodeProcess(int(self._handle))
130-
if code == _winapi.STILL_ACTIVE:
131-
raise
132-
self.returncode = code
133-
else:
134-
self.returncode = -signal.SIGTERM
125+
if self.returncode is not None:
126+
return
127+
128+
try:
129+
_winapi.TerminateProcess(int(self._handle), TERMINATE)
130+
except PermissionError:
131+
# ERROR_ACCESS_DENIED (winerror 5) is received when the
132+
# process already died.
133+
code = _winapi.GetExitCodeProcess(int(self._handle))
134+
if code == _winapi.STILL_ACTIVE:
135+
raise
136+
137+
# gh-113009: Don't set self.returncode. Even if GetExitCodeProcess()
138+
# returns an exit code different than STILL_ACTIVE, the process can
139+
# still be running. Only set self.returncode once WaitForSingleObject()
140+
# returns WAIT_OBJECT_0 in wait().
135141

136142
kill = terminate
137143

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

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.