I'm trying to get Tornado to stop after a timeout period without blocking any existing functionality. I might be missing a Tornado convention, but it doesn't matter if I use spawn_callback, Task, or a Thread, I seem to be holding up the main loop.
First, the reason I'm doing any of this is because I want to use the world famous NATS message bus in a client app to publish messages (not the usual straight HTTP functionality), then wait for the subscribed responses. Typical problem with async behaviour, and the official NATS Python client uses Tornado so I'm trying to work with it as well.
I suspect my problem has to do with understanding how the tornado.gen.coroutine decorator works with threading.
A clip of my code is below. If anyone notices my obvious issue, I'd appreciate a pointer. Thanks!
class Delayed(Thread):
def __init__(self, callback=None, timeout=2, *args, **kwargs):
super(Delayed, self).__init__(*args, **kwargs)
self.callback = callback
self.timeout = timeout
def run(self):
time.sleep(self.timeout)
if self.callback != None:
self.callback()
def timeout_task(timeout_secs=2):
time.sleep(timeout_secs)
ioloop.IOLoop.instance().stop()
yield
@tornado.gen.coroutine
def main():
parser = argparse.ArgumentParser()
parser.add_argument('CommandType')
...
parser.add_argument('-s', '--servers', default=[], action='append')
args = parser.parse_args()
try:
timeout=args.wait
servers = args.servers
queue = ""
...
if len(args.servers) < 1:
servers = ["nats://127.0.0.1:4222"]
data = funct_to_get_data()
nc = NATS()
opts = { "servers": servers }
yield nc.connect(**opts)
def self_stop():
ioloop.IOLoop.instance().stop()
def handler(msg):
print("[Received: {0}] {1}".format(msg.subject, msg.data))
print("Subscribed to '{0}'".format(subject))
future = nc.subscribe(subject, queue, handler)
sid = future.result()
yield nc.publish(subject, data)
yield nc.flush()
print("Published to '{0}'".format(subject))
# HERE is where I'd like to setup a non-blocking timeout that
# will stop Tornado.
# spawn_callback blocks and prevents future from receiving anything.
#ioloop.IOLoop.current().spawn_callback(lambda: timeout_task(timeout))
# Task blocks and prevents future from receiving anything.
yield tornado.gen.Task(timeout_task, timeout)
# Straight attempt at a Thread will block as well.
Delayed(self_stop(), timeout).start()
except Exception, e:
print(e)
show_usage_and_die()
if __name__ == '__main__':
main()
ioloop.IOLoop.instance().start()