Skip to content

Commit ed6250f

Browse files
committed
Attach processes run by executor to process list
Attach killing of manually set external child processes to executor
1 parent 032f76e commit ed6250f

File tree

2 files changed

+64
-17
lines changed

2 files changed

+64
-17
lines changed

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+
logger.debug(f"Terminating process {process.pid}: {process.args}")
350+
if not kill_children_only:
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: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -216,17 +216,20 @@ 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

224227
with contextlib.suppress(psutil.NoSuchProcess, psutil.ZombieProcess):
225228
for child in self._parent_process.children(recursive=True):
226-
if child not in _all_processes:
227-
_all_processes.append(child)
229+
if child not in process_list:
230+
process_list.append(child)
228231

229-
return list(set(_all_processes))
232+
return list(set(process_list))
230233

231234
def _get_sysinfo(self) -> dict[str, typing.Any]:
232235
"""Retrieve system administration
@@ -779,6 +782,13 @@ def kill_process(self, process_id: str) -> None:
779782

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

784794
@property

0 commit comments

Comments
 (0)