I noticed the asyncio library has a loop.add_signal_handler(signum, callback, *args)
method.
So far I have just been catching unix signals in the main file using the signals
module in with my asynchronous code like this:
signal.signal(signal.SIGHUP, callback)
async def main():
...
Is that an oversight on my part?
The add_signal_handler
documentation is sparse1, but looking at the source, it appears that the main added value compared to signal.signal
is that add_signal_handler
will ensure that the signal wakes up the event loop and allow the loop to invoke the signal handler along with other queued callbacks and runnable coroutines.
So far I have just been catching unix signals in the main file using the signals module [...] Is that an oversight on my part?
That depends on what the signal handler is doing. Printing a message or updating a global is fine, but if it is invoking anything in any way related to asyncio, it's most likely an oversight. A signal can be delivered at (almost) any time, including during execution of an asyncio callback, a coroutine, or even during asyncio's own bookkeeping.
For example, the implementation of asyncio.Queue
freely assumes that the access to the queue is single-threaded and non-reentrant. A signal handler adding something to a queue using q.put_nowait()
would be disastrous if it interrupted an on-going invocation of q.put_nowait()
on the same queue. Similar to typical race conditions experienced in multi-threaded code, an interruption in the middle of assignment to _unfinished_tasks
might well cause it to get incremented only once instead of twice (once for each put_nowait
).
Asyncio code is designed for cooperative multi-tasking, where the points where a function may suspend defined are clearly denoted by the await
and related keywords. The add_signal_handler
function ensures that your signal handler gets invoked at such a point, and that you're free to implement it as you'd implement any other asyncio callback.
add_signal_handler
documentation was briefer than today and didn't cover the difference to signal.signal
at all. This question prompted it getting expanded in the meantime.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With