2

I want to be able to stop and restart a Tornado server for testing and demo purposes. But it doesn't seem to release the port.

The following code is based on answer showing how to properly stop Tornado. I just added the code at the bottom that tries to restart Tornado. It fails with "error: Address in use" exception. I even added a call to ioloop.close() but that didn't help.

#! /usr/bin/env python

import threading
import tornado.ioloop
import tornado.web
import time


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world!\n")

def start_tornado(*args, **kwargs):
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    application.listen(8888)
    print "Starting Torando"
    tornado.ioloop.IOLoop.instance().start()
    print "Tornado finished"

def stop_tornado():
    ioloop = tornado.ioloop.IOLoop.instance()
    ioloop.add_callback(ioloop.stop)
    ioloop.add_callback(ioloop.close) # I added this but it didn't help.
    print "Asked Tornado to exit"

def main():

    t = threading.Thread(target=start_tornado)
    t.start()

    time.sleep(1)

    stop_tornado()
    t.join()
    print "Tornado thread stopped."

    t = threading.Thread(target=start_tornado) # Attempt restart.
    t.start()

if __name__ == "__main__":
    main()

Output:

Starting Torando
Asked Tornado to exit
 Tornado finished
Tornado thread stopped.
Exception in thread Thread-2:
    Traceback (most recent call last):
          File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "./test_tonado_restart.py", line 17, in start_tornado
    application.listen(8888)
  File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/site-packages/tornado/web.py", line 1825, in listen
    server.listen(port, address)
  File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/site-packages/tornado/tcpserver.py", line 126, in listen
    sockets = bind_sockets(port, address=address)
  File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/site-packages/tornado/netutil.py", line 196, in bind_sockets
    sock.bind(sockaddr)
  File "/home/mudd/musl/Python-2.7.11.install/lib/python2.7/socket.py", line 228, in meth
    return getattr(self._sock,name)(*args)
error: [Errno 98] Address in use
Community
  • 1
  • 1
JohnMudd
  • 13,607
  • 2
  • 26
  • 24

1 Answers1

3

Don't use threads like this unless you really need to - they complicate things quite a bit. For tests, use tornado.testing.AsyncTestCase or AsyncHTTPTestCase.

To free the port, you need to stop the HTTPServer, not just the IOLoop. In fact, you might not even need to stop the IOLoop at all. (but normally I'd restart everything by just letting the process exit and restarting it from scratch).

A non-threaded version of your example would be something like:

#! /usr/bin/env python

import tornado.ioloop
import tornado.web
import time


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world!\n")

def start_app(*args, **kwargs):
    application = tornado.web.Application([
        (r"/", MainHandler),
    ])
    server = application.listen(8888)
    print "Starting app"
    return server

def stop_tornado():
    ioloop = tornado.ioloop.IOLoop.current()
    ioloop.add_callback(ioloop.stop)
    print "Asked Tornado to exit"

def main():
    server = start_app()
    tornado.ioloop.IOLoop.current().add_timeout(
        datetime.timedelta(seconds=1),
        stop_tornado)
    tornado.ioloop.IOLoop.current().start()
    print "Tornado finished"
    server.stop()

    # Starting over
    start_app()
    tornado.ioloop.IOLoop.current().start()
Ben Darnell
  • 21,844
  • 3
  • 29
  • 50