async def start(self): """ Start the process if it isn't already running. If the process is already running, this is a noop. If the process has already been killed, this raises an exception """ # Aquire process lock before we try to start the process. # We could concurrently be in any other part of the code where # process is started or killed. So we check for that as soon # as we aquire the lock and behave accordingly. async with self._proc_lock: if self.running: # Don't wanna start it again, if we're already running return if self._killed: raise KilledProcessError("Process {} has already been explicitly killed".format(self.name)) self._debug_log('try-start', 'Trying to start {}', {}, self.name) self.proc = await asyncio.create_subprocess_exec( *self._proc_args, **self._proc_kwargs ) self._debug_log('started', 'Started {}', {}, self.name) self._killed = False self.running = True # Spin off a coroutine to watch, reap & restart process if needed # We don't wanna do this multiple times, so this is also inside the lock self._restart_process_future = asyncio.ensure_future(self._restart_process_if_needed()) # This handler is removed when process stops atexitasync.add_handler(self._handle_signal)
import asyncio import signal from functools import partial import sys from simpervisor import atexitasync def _handle_sigterm(number, received_signum): # Print the received signum so we know our handler was called print("handler {} received".format(number), int(received_signum), flush=True) handlercount = int(sys.argv[1]) for i in range(handlercount): atexitasync.add_handler(partial(_handle_sigterm, i)) loop = asyncio.get_event_loop() try: loop.run_forever() finally: # Cleanup properly so we get a clean exit try: remaining_tasks = asyncio.all_tasks(loop=loop) except AttributeError: # asyncio.all_tasks was added in 3. Provides reverse compatability. remaining_tasks = asyncio.Task.all_tasks(loop=loop) loop.run_until_complete(asyncio.gather(*remaining_tasks)) loop.close()