Let's assume we have such a trivial daemon written in python:
def mainloop():
while True:
# 1. do
# 2. some
# 3. important
# 4. job
# 5. sleep
mainloop()
and we daemonize it using start-stop-daemon
which by default sends SIGTERM
(TERM
) signal on --stop
.
Let's suppose the current step performed is #2
. And at this very moment we're sending TERM
signal.
What happens is that the execution terminates immediately.
I've found that I can handle the signal event using signal.signal(signal.SIGTERM, handler)
but the thing is that it still interrupts the current execution and passes the control to handler
.
So, my question is - is it possible to not interrupt the current execution but handle the TERM
signal in a separated thread (?) so that I was able to set shutdown_flag = True
so that mainloop()
had a chance to stop gracefully?
I think you are near to a possible solution.
Execute
mainloop
in a separate thread and extend it with the propertyshutdown_flag
. The signal can be caught withsignal.signal(signal.SIGTERM, handler)
in the main thread (not in a separate thread). The signal handler should setshutdown_flag
to True and wait for the thread to end withthread.join()
Here is a simple example without threads or classes.
Based on the previous answers, I have created a context manager which protects from sigint and sigterm.
Firstly I'm not certain that you need a second thread to set the shutdown_flag. Why not set it directly in the SIGTERM handler?
An alternative is to raise an exception from the
SIGTERM
handler, which will be propagated up the stack. Assuming you've got proper exception handling (e.g. withwith
/contextmanager
andtry: ... finally:
blocks) this should be a fairly graceful shutdown, similar to if you were toCtrl-C
your program.Example program
signals-test.py
:Now see the Ctrl-C behaviour:
This time I send it
SIGTERM
after 4 iterations withkill $(ps aux | grep signals-test | awk '/python/ {print $2}')
:This time I enable my custom
SIGTERM
handler and send itSIGTERM
:A class based clean to use solution: