4

I have python code which runs continuously (collecting sensor data). It is supposed to be launched at boot using start-stop-daemon. However, I'd like to be able to kill the process gracefully, so I've started from the advice in the post How to process SIGTERM signal gracefully? and put my main loop in a separate thread. I'd like to be able to gracefully shut it down both when it is running as a daemon (the start-stop-daemon will send a kill signal) and when I launch it briefly for testing in a terminal myself (me pressing ctrl-c).

However, the signal handler doesn't seem to be called if I kill the process (even without using the thread, the "done (killed)" never ends up in the file I've redirected to). And when I press ctrl-c, the collecting just continues and keeps printing data in the terminal (or to the file I am redirecting to).

What am I doing wrong in the following code?

from threading import Thread
import time, sys, signal

shutdown_flag = False #used for gracefull shutdown 

def main_loop():
    while not shutdown_flag:
        collect_data() # contains some print "data" statements
        time.sleep(5)
    print "done (killed)"

def sighandler(signum, frame):
    print 'signal handler called with signal: %s ' % signum
    global shutdown_flag
    shutdown_flag = True

def main(argv=None):
    signal.signal(signal.SIGTERM, sighandler) # so we can handle kill gracefully
    signal.signal(signal.SIGINT, sighandler) # so we can handle ctrl-c
    try:
        Thread(target=main_loop, args=()).start()
    except Exception, reason:
        print reason

if __name__ == '__main__':
    sys.exit(main(sys.argv))
Community
  • 1
  • 1
Rabarberski
  • 23,854
  • 21
  • 74
  • 96

1 Answers1

4

You are terminating your main thread with this statement:

if __name__ == '__main__':
    sys.exit(main(sys.argv))

So your signal handler never gets to run. The signal handler is part of the main thread not the main_loop thread you created. So once the main thread exits there's no signal handler function to call anymore.

You need something like this:

def sighandler(signum, frame):
    print 'signal handler called with signal: %s ' % signum
    global shutdown_flag
    shutdown_flag = True
    sys.exit() # make sure you add this so the main thread exits as well.

if __name__ == '__main__':
    main(sys.argv)
    while 1:  # this will force your main thread to live until you terminate it.
       time.sleep(1) 

A simple test to see how many threads are running in your program is this:

def main_loop():
    while not shutdown_flag:
        collect_data() # contains some print "data" statements
        time.sleep(5)
        import threading
        print threading.enumerate()
    print "done (killed)"
jramirez
  • 8,537
  • 7
  • 33
  • 46
  • But how can the main thread then end? (Indeed, sending `kill ` stopped the data output to the file, but the python process is still listed with the same when I do `ps`). Also, I thought I read somewhere that when using `Thread(..).start()` the calling thread waits for the spawned thread to finish (in contrast to `thread.start_new_thread` from the `thread` library) – Rabarberski Nov 14 '13 at 21:37
  • A thread can be flagged as a ``daemon thread''. The significance of this flag is that the entire Python program exits when only daemon threads are left. The initial value is inherited from the creating thread. The flag can be set with the setDaemon() method and retrieved with the isDaemon() method. – jramirez Nov 14 '13 at 21:49
  • The main thread, in this case is not waiting for the other thread to finish. Even if it's daemon thread if you exit the main thread it will wait for the other threads to finish, but no code in the main thread will be executed. Therefore you wont be able to call the sig handler. you need to keep your main thread alive. – jramirez Nov 14 '13 at 21:52
  • Hmm, this is sort of working, but not completely. When I do a `kill ` from another ssh session, indeed I see that the main_loop thread is stopped (and "done (killed)") is printed. But the main thread remains running, with the signal handler now printing correctly the called signal. Wether I launch the main_loop thread as a daemon or not makes no change in this behavior. The behavior I see seems logical to me, as the main thread can not end if we have a `while 1` loop, no? So the question remains... – Rabarberski Nov 14 '13 at 22:11
  • 1
    oops yes I forgot you need a `sys.exit()` at the end in your `sighandler` function. That should force the main loop to exit – jramirez Nov 14 '13 at 22:14
  • Yes, right!! Now it is working. Making it a daemon thread was not necessary. Will you update your answer? – Rabarberski Nov 14 '13 at 22:18