Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Catching the TERM signal in Gevent based applications

I wrote a simple daemon in Python which is based on Gevent. The daemon needs to do some cleanup before it exists, so it is necessary that it can handle the TERM signal, preform its cleanup and exit gracefully. In a single threaded daemon which is not based on Gevent, I used the Python's signal module to set a signal handler for the TERM signal. The signal handler throws an user defined exception which is called TermSignal. The main thread of the daemon can just catch the TermSignal exception, do its cleanup and exit.

This solution did not work as expected when I tried to implement it in a daemon based on Gevent. The daemon has a main greenlet that calls joinall on the workers greenlets. the joinall call is wrapped in try/except block which catches the KeyboardInterrupt, which allows the daemon to preform its cleanup when it runs without daemonizing. However, when I implemented the solution above and sent a TERM signal to the process, I could see in the console window that one of the worker greenlets raised the TermSignal exception instead of the main greenlet. This uncaught exception did not proagate to the main greenlet, despite the face that I called joinall with raise_error parameter set to True. The result was that one of the worker greenlets crashed due to the exception, but the daemon did not exit at all.

like image 417
reish Avatar asked Dec 17 '13 09:12

reish


1 Answers

After I did some searching I found the solution. As mentioned here, Gevent's monkey patching does not patch Python's built-in signal.signal function. In order for my solution to work, the main greenlet must call gevent.signal instead of signal.signal in order to set the handler. Also, gevent.signal expects handler functions which accept no arguments, unlike signal.signal which expects handler functions which accept 2 arguments. Since I don't care about these arguments anyway, I changed my term handler to look like this:

def _term_handler(*_):
    raise TermSignal()

So that the same term handle can be used in Gevent application and in regular Python applications. Although the same handler can be used in both cases, it needs to be set by the appropriate function (signal.signal or gevent.signal)

like image 133
reish Avatar answered Nov 01 '22 05:11

reish