0

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?

loknar
  • 539
  • 5
  • 12
  • Do the background threads have any interaction with the IOLoop or any other part of Tornado? How do background threads communicate with the actual web server? – A. Jesse Jiryu Davis Dec 05 '15 at 14:24
  • all background threads communicate via special object - bus, through signals/events and requests, every object has same bus instance. I'm not sure what do you mean by interacting with IOLoop, since i only start it and it hangs, serving web server. And this error happens when i want to stop IOLoop from main thread. – loknar Dec 05 '15 at 19:35

1 Answers1

3

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.

Community
  • 1
  • 1
kwarunek
  • 12,141
  • 4
  • 43
  • 48
  • 1
    In addition, `__call__` should not call `IOLoop.stop` (after `start()` has returned, the loop is already stopped), and the call to `IOLoop.close` should be in `__call__`, not `WebServer.stop`. See the note in http://www.tornadoweb.org/en/stable/ioloop.html#tornado.ioloop.IOLoop.close – Ben Darnell Dec 05 '15 at 22:40
  • That perfectly worked, thank you! Sorry for minimum working example was not provided (my code is very big to fit here), will point attention on this next time and prepare some simple one. – loknar Dec 06 '15 at 11:41