context manager work for startup/shutdown
This commit is contained in:
@@ -5,41 +5,38 @@ import signal
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from contextlib import ExitStack, contextmanager
|
||||
from dataclasses import dataclass, field
|
||||
from threading import Event, Lock
|
||||
from threading import Event
|
||||
from typing import Any, Callable
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def handler(signum, frame):
|
||||
print('Signal handler called with signal', signum)
|
||||
raise OSError("Couldn't open device!")
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppDaemonRunContext:
|
||||
_stack: ExitStack = field(default_factory=ExitStack)
|
||||
stop_event: Event = field(default_factory=Event)
|
||||
shutdown_lock: Lock = field(default_factory=Lock)
|
||||
shutdown_grace_period: float = 1.0
|
||||
shutdown_grace_period: float = 0.75
|
||||
|
||||
loop: asyncio.AbstractEventLoop = field(init=False)
|
||||
executor: ThreadPoolExecutor = field(init=False)
|
||||
|
||||
def __enter__(self):
|
||||
self.loop = self._stack.enter_context(self.asyncio_context())
|
||||
logger.debug("Entered asyncio context")
|
||||
logger.debug("Created event loop")
|
||||
|
||||
signals = (signal.SIGHUP, signal.SIGTERM, signal.SIGINT)
|
||||
for s in signals:
|
||||
self.loop.add_signal_handler(s, lambda s=s: self.loop.create_task(self.shutdown(s)))
|
||||
|
||||
self.executor = self._stack.enter_context(self.thread_context())
|
||||
logger.debug("Entered threadpool context")
|
||||
logger.debug("Started thread pool")
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self._stack.__exit__(exc_type, exc_value, traceback)
|
||||
logger.debug("Exited context")
|
||||
logger.debug(f'Closing context from {self.__class__.__name__}')
|
||||
self._stack.close()
|
||||
|
||||
def get_running_tasks(self, exclude_current: bool = True) -> list[asyncio.Task]:
|
||||
return [
|
||||
@@ -47,7 +44,7 @@ class AppDaemonRunContext:
|
||||
if exclude_current and t is not asyncio.current_task()
|
||||
]
|
||||
|
||||
async def shutdown(self, signal):
|
||||
async def shutdown(self, signal=signal.SIGTERM):
|
||||
"""Cleanup tasks tied to the service's shutdown.
|
||||
|
||||
https://www.roguelynn.com/words/asyncio-graceful-shutdowns/
|
||||
@@ -68,9 +65,9 @@ class AppDaemonRunContext:
|
||||
|
||||
for task in tasks:
|
||||
if task.cancelled():
|
||||
logger.warning(f'Cancelled {task.get_coro().__qualname__}')
|
||||
logger.warning(f'Cancelled {task.get_name()}')
|
||||
|
||||
logger.debug("Stopping event loop in context shutdown")
|
||||
logger.info("Stopping asyncio event loop")
|
||||
self.loop.stop()
|
||||
else:
|
||||
logger.warning('Already started shutdown')
|
||||
@@ -83,14 +80,14 @@ class AppDaemonRunContext:
|
||||
finally:
|
||||
loop.close()
|
||||
if loop.is_closed():
|
||||
logger.debug("Closed the event loop.")
|
||||
logger.debug("Gracefully closed event loop.")
|
||||
|
||||
@contextmanager
|
||||
def thread_context(self):
|
||||
with ThreadPoolExecutor(max_workers=5) as executor:
|
||||
yield executor
|
||||
if executor._shutdown:
|
||||
logger.debug('Shut down the ThreadPoolExecutor')
|
||||
logger.debug('Gracefully shut down ThreadPoolExecutor')
|
||||
|
||||
async def run_in_executor(
|
||||
self,
|
||||
|
||||
Reference in New Issue
Block a user