Search code examples
pythonwebservertornadopython-multithreading

Tornado web server ioloop error on .stop()


I have web server on built on tornado. Here are some parts of main WebServer class:

import tornado.ioloop as tio
...

class WebServer(object):

    def __init__(.....):
        ...
        self.IOloop = tio.IOLoop.instance()
        ...

    def __call__(self):
        self.logger('info', 'Web server', 'Starting web server on port %s, waiting for connection...' % (self.port))
        if self.verbose:
            print 'Starting web server on port %s, waiting for connection...' % (self.port)
        try:
            self.application.listen(self.port)
            self.IOloop.start()
        except Exception as error:
            self.logger('error', 'Web server', str(error), exc_info = True)
        self.stop()
        if self.verbose:
            print 'Web server shut down'
        self.logger('info', 'Web server', 'Stopped')

    def stop(self):
        if self.up:
            self.logger('info', 'Web server', 'Shutting down...')
            ioloop = tio.IOLoop.current()
            ioloop.stop()
            ioloop.close()
            self.ws_controller.stop()
            self.ws_thread.join()
            self.up = False

Instance of Server is one of alot of various backend objects running. I launch server instance as well as other backend objects within separate thread:

def assign_component(self, name, config_name, class_):
    self.components[name] = class_(bus = self.bus, logger = self.logger, config = self.config[config_name], verbose = self.verbose)
    setattr(self, name, self.components[name])
    self.threads[name] = thr.Thread(target = self.components[name])
    self.threads[name].start()

Where class_ is WebServer in this case. Its working with no problems. But when i want to stop the whole application, i encounter following error:

ERROR:tornado.application:Exception in callback None
  Traceback (most recent call last):
    File "/root/anaconda2/lib/python2.7/site-packages/tornado/ioloop.py", line 865, in start
      fd_obj, handler_func = self._handlers[fd]
KeyError: 29

I read through many articles, they tell something about thread unsafety and add_callback, but i dont get why would i need some callbacks if i just want to start and stop the main instance of IOloop? What am i missing in understanding of tornado IOloop?


Solution

  • The only thread-safe method in the IOLoop is add_callback. You can use it to stop IOLoop. However, not tested, it would be similar to:

    ...
    def stop(self):
        if self.up:
            self.logger('info', 'Web server', 'Shutting down...')
            self.IOLoop.add_callback(lambda x: x.stop(), self.IOLoop)
            self.ws_controller.stop()
            self.ws_thread.join()
            self.up = False
    

    Some related question: How do I stop Tornado web server?

    P.S. It is good habit to provide minimum working example.