From 032f76e72336484a35650398b449935887f5d079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Wed, 12 Jun 2024 09:43:24 +0100 Subject: [PATCH 1/4] Remove dev from version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 25c1138b..6f9d67a2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "simvue" -version = "1.0.0.dev2" +version = "1.0.0" description = "Simulation tracking and monitoring" authors = ["Simvue Development Team "] license = "Apache v2" From ed6250fae2288e8472452ba091de57b1605734a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Wed, 12 Jun 2024 10:04:08 +0100 Subject: [PATCH 2/4] Attach processes run by executor to process list Attach killing of manually set external child processes to executor --- simvue/executor.py | 61 +++++++++++++++++++++++++++++++++++++--------- simvue/run.py | 20 +++++++++++---- 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/simvue/executor.py b/simvue/executor.py index b9682454..dcfbb508 100644 --- a/simvue/executor.py +++ b/simvue/executor.py @@ -11,6 +11,7 @@ __date__ = "2023-11-15" import logging +import contextlib import multiprocessing.synchronize import sys import multiprocessing @@ -205,6 +206,24 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None: name=f"{identifier}_exit_status", source="user" ) + @property + def processes(self) -> list[psutil.Process]: + """Create an array containing a list of processes""" + if not self._processes: + return [] + + _all_processes: list[psutil.Process] = [ + psutil.Process(process.pid) for process in self._processes.values() + ] + + with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess): + for process in _all_processes: + for child in process.children(recursive=True): + if child not in _all_processes: + _all_processes.append(child) + + return list(set(_all_processes)) + @property def success(self) -> int: """Return whether all attached processes completed successfully""" @@ -294,15 +313,31 @@ def _save_output(self) -> None: f"{self._runner.name}_{proc_id}.out", category="output" ) - def kill_process(self, process_id: str) -> None: - """Kill a running process by ID""" - if not (process := self._processes.get(process_id)): - logger.error( - f"Failed to terminate process '{process_id}', no such identifier." - ) - return + def kill_process( + self, process_id: typing.Union[int, str], kill_children_only: bool = False + ) -> None: + """Kill a running process by ID - parent = psutil.Process(process.pid) + If argument is a string this is a process handled by the client, + else it is a PID of a external monitored process + + Parameters + ---------- + process_id : typing.Union[int, str] + either the identifier for a client created process or the PID + of an external process + kill_children_only : bool, optional + if process_id is an integer, whether to kill only its children + """ + if isinstance(process_id, str): + if not (process := self._processes.get(process_id)): + logger.error( + f"Failed to terminate process '{process_id}', no such identifier." + ) + return + parent = psutil.Process(process.pid) + elif isinstance(process_id, int): + parent = psutil.Process(process_id) for child in parent.children(recursive=True): logger.debug(f"Terminating child process {child.pid}: {child.name()}") @@ -311,11 +346,13 @@ def kill_process(self, process_id: str) -> None: for child in parent.children(recursive=True): child.wait() - logger.debug(f"Terminating child process {process.pid}: {process.args}") - process.kill() - process.wait() + logger.debug(f"Terminating process {process.pid}: {process.args}") + if not kill_children_only: + process.kill() + process.wait() - self._execute_callback(process_id) + if isinstance(process_id, str): + self._execute_callback(process_id) def kill_all(self) -> None: """Kill all running processes""" diff --git a/simvue/run.py b/simvue/run.py index 23deef9f..05207230 100644 --- a/simvue/run.py +++ b/simvue/run.py @@ -216,17 +216,20 @@ def time_stamp(self) -> str: @property def processes(self) -> list[psutil.Process]: """Create an array containing a list of processes""" + + process_list = self._executor.processes + if not self._parent_process: - return [] + return process_list - _all_processes: list[psutil.Process] = [self._parent_process] + process_list += [self._parent_process] with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess): for child in self._parent_process.children(recursive=True): - if child not in _all_processes: - _all_processes.append(child) + if child not in process_list: + process_list.append(child) - return list(set(_all_processes)) + return list(set(process_list)) def _get_sysinfo(self) -> dict[str, typing.Any]: """Retrieve system administration @@ -779,6 +782,13 @@ def kill_process(self, process_id: str) -> None: def kill_all_processes(self) -> None: """Kill all currently running processes.""" + # Dont kill the manually attached process if it is the current script + # but do kill its children. The kill process method of executor by + # default refers to its own processes but can also be used on a PID + if self._parent_process: + self._executor.kill_process( + self._parent_process.pid, self._parent_process == os.getpid() + ) self._executor.kill_all() @property From a787a747bae30d75f39d8de7acf7a1f6344cb4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Wed, 12 Jun 2024 10:09:57 +0100 Subject: [PATCH 3/4] Fixed log message position --- simvue/executor.py | 2 +- simvue/run.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/simvue/executor.py b/simvue/executor.py index dcfbb508..3376bf96 100644 --- a/simvue/executor.py +++ b/simvue/executor.py @@ -346,8 +346,8 @@ def kill_process( for child in parent.children(recursive=True): child.wait() - logger.debug(f"Terminating process {process.pid}: {process.args}") if not kill_children_only: + logger.debug(f"Terminating process {process.pid}: {process.args}") process.kill() process.wait() diff --git a/simvue/run.py b/simvue/run.py index 05207230..38492823 100644 --- a/simvue/run.py +++ b/simvue/run.py @@ -224,6 +224,7 @@ def processes(self) -> list[psutil.Process]: process_list += [self._parent_process] + # Attach child processes relating to the process set by set_pid with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess): for child in self._parent_process.children(recursive=True): if child not in process_list: From f973470f9b7174aa94c57a677a398bf224c7fc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kristian=20Zar=C4=99bski?= Date: Wed, 12 Jun 2024 10:11:54 +0100 Subject: [PATCH 4/4] Fix wrong comparison in process PID check --- simvue/run.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/simvue/run.py b/simvue/run.py index 38492823..30c4466f 100644 --- a/simvue/run.py +++ b/simvue/run.py @@ -788,7 +788,8 @@ def kill_all_processes(self) -> None: # default refers to its own processes but can also be used on a PID if self._parent_process: self._executor.kill_process( - self._parent_process.pid, self._parent_process == os.getpid() + process_id=self._parent_process.pid, + kill_children_only=self._parent_process.pid == os.getpid(), ) self._executor.kill_all()