| from __future__ import annotations | |
| import subprocess | |
| from typing import Any | |
| def set_process_tree_paused(process: subprocess.Popen, paused: bool) -> bool: | |
| """Suspend/resume a trainer process tree when psutil is available.""" | |
| try: | |
| import psutil | |
| parent = psutil.Process(process.pid) | |
| children = parent.children(recursive=True) | |
| targets = [*children, parent] if paused else [parent, *children] | |
| for target in targets: | |
| try: | |
| target.suspend() if paused else target.resume() | |
| except (psutil.NoSuchProcess, psutil.AccessDenied): | |
| continue | |
| return True | |
| except Exception: | |
| return False | |
| def terminate_process_tree(process: Any, *, timeout: float = 3.0) -> None: | |
| """Terminate a process and any children it launched.""" | |
| try: | |
| import psutil | |
| parent = psutil.Process(process.pid) | |
| children = parent.children(recursive=True) | |
| targets = [*children, parent] | |
| for target in targets: | |
| try: | |
| target.terminate() | |
| except (psutil.NoSuchProcess, psutil.AccessDenied): | |
| continue | |
| _gone, alive = psutil.wait_procs(targets, timeout=timeout) | |
| for target in alive: | |
| try: | |
| target.kill() | |
| except (psutil.NoSuchProcess, psutil.AccessDenied): | |
| continue | |
| return | |
| except Exception: | |
| pass | |
| try: | |
| process.terminate() | |
| except Exception: | |
| return | |
| try: | |
| process.wait(timeout=timeout) | |
| except Exception: | |
| try: | |
| process.kill() | |
| except Exception: | |
| pass | |