Skip to content

Commit f51382a

Browse files
committed
Kill Windows process tree on subprocess timeout to unblock proc.communicate()
When a subprocess times out on Windows, proc.kill() only kills the direct child. Grandchild processes (e.g. installer EXEs spawned by winget) inherit the pipe write handles and keep them open, causing the subsequent proc.communicate() call to block indefinitely until every grandchild exits. Fix by calling taskkill /F /T /PID before proc.communicate() to kill the entire process tree atomically. Also run windows_processes_to_cleanup in the timeout path, mirroring what already happens in the normal exit path.
1 parent 4a98354 commit f51382a

1 file changed

Lines changed: 18 additions & 0 deletions

File tree

meta_package_manager/base.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -827,6 +827,24 @@ def run(
827827
)
828828
except subprocess.TimeoutExpired:
829829
logging.debug(f"PID {proc.pid} timed out; sending kill.")
830+
if is_any_windows():
831+
# Kill the entire process tree before proc.communicate() to
832+
# avoid blocking indefinitely: grandchild installer
833+
# processes (spawned by e.g. winget) inherit pipe handles
834+
# and keep them open even after proc.kill(), so
835+
# proc.communicate() would block until every grandchild
836+
# exits on its own.
837+
subprocess.run(
838+
("taskkill", "/F", "/T", "/PID", str(proc.pid)),
839+
capture_output=True,
840+
timeout=10,
841+
)
842+
for proc_name in self.windows_processes_to_cleanup:
843+
subprocess.run(
844+
("taskkill", "/F", "/IM", proc_name),
845+
capture_output=True,
846+
timeout=5,
847+
)
830848
proc.kill()
831849
stdout, stderr = proc.communicate()
832850
logging.debug(f"PID {proc.pid} killed; exit {proc.returncode}.")

0 commit comments

Comments
 (0)