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.
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
.