Skip to content

Commit 2a115cc

Browse files
Merge pull request #416 from simvue-io/hotfix/manual-added-processes
Attach manual processes to executor and executor processes to resource monitoring
2 parents 5f3156e + f973470 commit 2a115cc

File tree

3 files changed

+67
-18
lines changed

3 files changed

+67
-18
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "simvue"
3-
version = "1.0.0.dev2"
3+
version = "1.0.0"
44
description = "Simulation tracking and monitoring"
55
authors = ["Simvue Development Team <[email protected]>"]
66
license = "Apache v2"

simvue/executor.py

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
__date__ = "2023-11-15"
1212

1313
import logging
14+
import contextlib
1415
import multiprocessing.synchronize
1516
import sys
1617
import multiprocessing
@@ -205,6 +206,24 @@ def callback_function(status_code: int, std_out: str, std_err: str) -> None:
205206
name=f"{identifier}_exit_status", source="user"
206207
)
207208

209+
@property
210+
def processes(self) -> list[psutil.Process]:
211+
"""Create an array containing a list of processes"""
212+
if not self._processes:
213+
return []
214+
215+
_all_processes: list[psutil.Process] = [
216+
psutil.Process(process.pid) for process in self._processes.values()
217+
]
218+
219+
with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess):
220+
for process in _all_processes:
221+
for child in process.children(recursive=True):
222+
if child not in _all_processes:
223+
_all_processes.append(child)
224+
225+
return list(set(_all_processes))
226+
208227
@property
209228
def success(self) -> int:
210229
"""Return whether all attached processes completed successfully"""
@@ -294,15 +313,31 @@ def _save_output(self) -> None:
294313
f"{self._runner.name}_{proc_id}.out", category="output"
295314
)
296315

297-
def kill_process(self, process_id: str) -> None:
298-
"""Kill a running process by ID"""
299-
if not (process := self._processes.get(process_id)):
300-
logger.error(
301-
f"Failed to terminate process '{process_id}', no such identifier."
302-
)
303-
return
316+
def kill_process(
317+
self, process_id: typing.Union[int, str], kill_children_only: bool = False
318+
) -> None:
319+
"""Kill a running process by ID
304320
305-
parent = psutil.Process(process.pid)
321+
If argument is a string this is a process handled by the client,
322+
else it is a PID of a external monitored process
323+
324+
Parameters
325+
----------
326+
process_id : typing.Union[int, str]
327+
either the identifier for a client created process or the PID
328+
of an external process
329+
kill_children_only : bool, optional
330+
if process_id is an integer, whether to kill only its children
331+
"""
332+
if isinstance(process_id, str):
333+
if not (process := self._processes.get(process_id)):
334+
logger.error(
335+
f"Failed to terminate process '{process_id}', no such identifier."
336+
)
337+
return
338+
parent = psutil.Process(process.pid)
339+
elif isinstance(process_id, int):
340+
parent = psutil.Process(process_id)
306341

307342
for child in parent.children(recursive=True):
308343
logger.debug(f"Terminating child process {child.pid}: {child.name()}")
@@ -311,11 +346,13 @@ def kill_process(self, process_id: str) -> None:
311346
for child in parent.children(recursive=True):
312347
child.wait()
313348

314-
logger.debug(f"Terminating child process {process.pid}: {process.args}")
315-
process.kill()
316-
process.wait()
349+
if not kill_children_only:
350+
logger.debug(f"Terminating process {process.pid}: {process.args}")
351+
process.kill()
352+
process.wait()
317353

318-
self._execute_callback(process_id)
354+
if isinstance(process_id, str):
355+
self._execute_callback(process_id)
319356

320357
def kill_all(self) -> None:
321358
"""Kill all running processes"""

simvue/run.py

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,17 +216,21 @@ def time_stamp(self) -> str:
216216
@property
217217
def processes(self) -> list[psutil.Process]:
218218
"""Create an array containing a list of processes"""
219+
220+
process_list = self._executor.processes
221+
219222
if not self._parent_process:
220-
return []
223+
return process_list
221224

222-
_all_processes: list[psutil.Process] = [self._parent_process]
225+
process_list += [self._parent_process]
223226

227+
# Attach child processes relating to the process set by set_pid
224228
with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess):
225229
for child in self._parent_process.children(recursive=True):
226-
if child not in _all_processes:
227-
_all_processes.append(child)
230+
if child not in process_list:
231+
process_list.append(child)
228232

229-
return list(set(_all_processes))
233+
return list(set(process_list))
230234

231235
def _get_sysinfo(self) -> dict[str, typing.Any]:
232236
"""Retrieve system administration
@@ -779,6 +783,14 @@ def kill_process(self, process_id: str) -> None:
779783

780784
def kill_all_processes(self) -> None:
781785
"""Kill all currently running processes."""
786+
# Dont kill the manually attached process if it is the current script
787+
# but do kill its children. The kill process method of executor by
788+
# default refers to its own processes but can also be used on a PID
789+
if self._parent_process:
790+
self._executor.kill_process(
791+
process_id=self._parent_process.pid,
792+
kill_children_only=self._parent_process.pid == os.getpid(),
793+
)
782794
self._executor.kill_all()
783795

784796
@property

0 commit comments

Comments
 (0)