In a multithreaded Python program, one thread sometimes asks for console input using the built-in raw_input(). I'd like to be able to be able to close the program while at a raw_input prompt by typing ^C at the shell (i.e., with a SIGINT signal). However, when the child thread is executing raw_input, typing ^C does nothing -- the KeyboardInterrupt is not raised until I hit return (leaving raw_input).
For example, in the following program:
import threading
class T(threading.Thread):
def run(self):
x = raw_input()
print x
if __name__ == '__main__':
t = T()
t.start()
t.join()
Typing ^C does nothing until after the input is finished. However, if we just call T().run()
(i.e., the single-threaded case: just run raw_input in the main thread), ^C closes the program immediately.
Presumably, this is because SIGINT is sent to the main thread, which is suspended (waiting for the GIL) while the forked thread blocks on the console read. The main thread does not get to execute its signal handler until it grabs the GIL after raw_input returns. (Please correct me if I'm wrong about this -- I'm not an expert on Python's threading implementation.)
Is there a way to read from stdin in a raw_input-like way while allowing the SIGINT to be handled by the main thread and thus bring down the whole process?
[I've observed the behavior above on Mac OS X and a few different Linuxes.]
Edit: I've mischaracterized the underlying problem above. On further investigation, it's the main thread's call to join()
that's preventing signal handling: Guido van Rossum himself has explained that the underlying lock acquire in join is uninterruptible. This means that the signal is actually being deferred until the entire thread finishes -- so this really has nothing to do with raw_input
at all (just the fact that the background thread is blocking so that the join does not complete).