mirror of
ssh://git.janware.com/srv/git/janware/proj/jw-python
synced 2026-01-15 01:52:56 +01:00
Add Process and Signals support
Signed-off-by: Jan Lindemann <jan@janware.com>
This commit is contained in:
parent
6123a68195
commit
c96ffe52c0
6 changed files with 139 additions and 0 deletions
68
tools/python/jwutils/Process.py
Normal file
68
tools/python/jwutils/Process.py
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from enum import Enum, Flag, auto
|
||||||
|
|
||||||
|
def _sigchld_handler(signum, process):
|
||||||
|
if not signum == signal.SIGCHLD:
|
||||||
|
return
|
||||||
|
Process.propagate_signal(signum)
|
||||||
|
|
||||||
|
class Process(ABC): # export
|
||||||
|
|
||||||
|
__processes = []
|
||||||
|
|
||||||
|
class State(Enum):
|
||||||
|
Running = auto()
|
||||||
|
Shutdown = auto()
|
||||||
|
Done = auto()
|
||||||
|
|
||||||
|
class Flags(Flag):
|
||||||
|
FailOnExitWithoutShutdown = auto()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.__state = Running
|
||||||
|
self.__flags = Flags.FailOnExitWithoutShutdown
|
||||||
|
if len(self.__processes) == 0:
|
||||||
|
self._signals().add_handler(signals.SIGCHLD, _sigchld_handler)
|
||||||
|
self.__processes.add(self)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def propagate_signal(cls, signum):
|
||||||
|
for p in cls.__processes:
|
||||||
|
p.__signal(signum)
|
||||||
|
|
||||||
|
def signal(self, signum):
|
||||||
|
if signum == signals.SIGCHLD:
|
||||||
|
self.exited()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def _pid(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def signals(cls):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# to be reimplemented
|
||||||
|
def _request_shutdown(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# to be reimplemented
|
||||||
|
def name(self):
|
||||||
|
return str(self._pid())
|
||||||
|
|
||||||
|
def request_shutdown(self):
|
||||||
|
if not self.__state == Shutdown:
|
||||||
|
self.__state = Shutdown
|
||||||
|
self._request_shutdown()
|
||||||
|
|
||||||
|
def exited(self):
|
||||||
|
if self.__state == Process.State.Running:
|
||||||
|
slog(ERR, 'process "{}" exited unexpectedly'.format(process.name()))
|
||||||
|
if __flags & Process.Flags.FailOnExitWithoutShutdown:
|
||||||
|
slog(ERR, 'exiting')
|
||||||
|
exit(1)
|
||||||
|
self.__state = Process.State.Done
|
||||||
|
self.__processes.erase(self)
|
||||||
|
if len(self.__processes) == 0:
|
||||||
|
self._signals().remove_handler(signals.SIGCHLD) # FIXME: broken logic
|
||||||
34
tools/python/jwutils/Signals.py
Normal file
34
tools/python/jwutils/Signals.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
_handled_signals = {}
|
||||||
|
|
||||||
|
def _signal_handler(signal, frame):
|
||||||
|
if not signal in _handled_signals.keys():
|
||||||
|
return
|
||||||
|
for h in _handled_signals[signal]:
|
||||||
|
h.func(signal, *h.args)
|
||||||
|
|
||||||
|
class Signals:
|
||||||
|
|
||||||
|
class Handler:
|
||||||
|
def __init__(self, func, args):
|
||||||
|
self.func = func
|
||||||
|
self.args = args
|
||||||
|
|
||||||
|
def __init(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def _add_handler(self, signal, handler):
|
||||||
|
raise Exception("_add_handler() is not reimplemented")
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def add_handler(cls, signals, handler, *args):
|
||||||
|
for signal in signals:
|
||||||
|
h = Signals.Handler(handler, args)
|
||||||
|
if not signal in _handled_signals.keys():
|
||||||
|
_handled_signals[signal] = [h]
|
||||||
|
cls._add_signal_handler(signal, _signal_handler)
|
||||||
|
else:
|
||||||
|
_handled_signals[signal].add(h)
|
||||||
4
tools/python/jwutils/asyncio/Makefile
Normal file
4
tools/python/jwutils/asyncio/Makefile
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
TOPDIR = ../../../..
|
||||||
|
|
||||||
|
include $(TOPDIR)/make/proj.mk
|
||||||
|
include $(JWBDIR)/make/py-mod.mk
|
||||||
20
tools/python/jwutils/asyncio/Process.py
Normal file
20
tools/python/jwutils/asyncio/Process.py
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
from ..Process import Process as ProcessBase
|
||||||
|
from .Signals import Signals
|
||||||
|
|
||||||
|
class Process(ProcessBase): # export
|
||||||
|
|
||||||
|
__signals = Signals()
|
||||||
|
|
||||||
|
def __init__(self, aio):
|
||||||
|
super().__init()
|
||||||
|
self.aio = aio
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
@abstractmethod
|
||||||
|
def signals(cls):
|
||||||
|
return cls.__signals
|
||||||
|
|
||||||
|
def wait(self):
|
||||||
|
return self.aio.wait()
|
||||||
10
tools/python/jwutils/asyncio/Signals.py
Normal file
10
tools/python/jwutils/asyncio/Signals.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
import asyncio
|
||||||
|
from ..Signals import Signals as SignalsBase
|
||||||
|
|
||||||
|
class Signals(SignalsBase):
|
||||||
|
|
||||||
|
# reimplemented from Signals
|
||||||
|
@classmethod
|
||||||
|
def _add_handler(cls, signal, handler):
|
||||||
|
loop = asyncio.get_running_loop()
|
||||||
|
loop.add_signal_handler(signal, handler, None) # None = *args
|
||||||
3
tools/python/jwutils/asyncio/__init__.py
Normal file
3
tools/python/jwutils/asyncio/__init__.py
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# >> -------------------------- generated by python-tools.sh >>
|
||||||
|
from jwutils.asyncio.Process import Process
|
||||||
|
# << -------------------------- generated by python-tools.sh <<
|
||||||
Loading…
Add table
Add a link
Reference in a new issue