Search code examples
pythontornado

Close and reset Tornado IOLoop after KeyboardInterrupt


I occasionally want to expose logic within tornado coroutines to interactive users. These users often press Ctrl-C to interrupt long-running computations. This puts the IOLoop in an unhappy state for future interactions.

In [1]: from tornado import gen
In [2]: from tornado.ioloop import IOLoop
In [3]: IOLoop.current().run_sync(lambda: gen.sleep(100000))
KeyboardInterrupt: 

In [4]: IOLoop.current().run_sync(lambda: gen.sleep(1))
RuntimeError: IOLoop is already running

What operations should I do in between [3] and [4] to clean up or replace the stuck IOLoop. Some combination of .stop, .close, .close_current and .close_instance have worked for me in the past.


Solution

  • After a KeyboardInterrupt, the IOLoop is left in an undefined state and cannot be safely restarted. (This is true of most objects: it is rare for an object to still be usable after a method has been interrupted unless that method was read-only).

    If you want to be able to recover from C-c you'll have to write your own signal handler instead of using the default one which raises KeyboardInterrupt. For example, to simply stop the IOLoop and leave it in a restartable state, you could do something like this (untested):

    def sigint_handler(sig, frame):
        io_loop = IOLoop.current()
        io_loop.add_callback_from_signal(io_loop.stop())
    signal.signal(signal.SIGINT, sigint_handler)
    

    If you want to raise an exception after being interrupted in this way, you'll have to make changes to the code that calls IOLoop.start.