Search code examples
pythonmultithreadingtornado

Tornado error: cannot switch to a different thread


A request comes to tornado's GET handler of a web app. From the GET function, a blocking_task function is called. This blocking_task function has @run_on_executor decorator.

But this execution fails. Could you please help on this. It seems that motor db is not able to execute the thread.

import time
from concurrent.futures import ThreadPoolExecutor
from tornado import gen, web
from tornado.concurrent import run_on_executor
from tornado.ioloop import IOLoop
import argparse
from common.config import APIConfig
import sys
import os
import motor

parser = argparse.ArgumentParser()
parser.add_argument("-c", "--config-file", dest='config_file',
                    help="Config file location")
args = parser.parse_args()
CONF = APIConfig().parse(args.config_file)

client = motor.MotorClient(CONF.mongo_url)
db = client[CONF.mongo_dbname]
class Handler(web.RequestHandler):

    executor = ThreadPoolExecutor(10)

    def initialize(self):
        """ Prepares the database for the entire class """
        self.db = self.settings["db"]

    @gen.coroutine
    def get(self):
        self.blocking_task()

    @run_on_executor
    def blocking_task(self):
        mongo_dict = self.db.test_cases.find_one({"name": "Ping"})


if __name__ == "__main__":
    app = web.Application([
        (r"/", Handler),
    ],
        db=db,
        debug=CONF.api_debug_on,
    )
    app.listen(8888)
    IOLoop.current().start()




> ERROR:tornado.application:Exception in callback <functools.partial
> object at 0x7f72dfbe48e8> Traceback (most recent call last):   File
> "/usr/local/lib/python2.7/dist-packages/tornado-4.3-py2.7-linux-x86_64.egg/tornado/ioloop.py",
> line 600, in _run_callback
>     ret = callback()   File "/usr/local/lib/python2.7/dist-packages/tornado-4.3-py2.7-linux-x86_64.egg/tornado/stack_context.py",
> line 275, in null_wrapper
>     return fn(*args, **kwargs)   File "/usr/local/lib/python2.7/dist-packages/motor-0.5-py2.7.egg/motor/frameworks/tornado.py",
> line 231, in callback
>     child_gr.switch(future.result()) error: cannot switch to a different thread

Could you please help on this.


Solution

  • Finally following code works, Thank you @kwarunek Also added parameters to the callback function.

    import time
    from concurrent.futures import ThreadPoolExecutor
    from tornado import gen, web
    from tornado.concurrent import run_on_executor
    from tornado.ioloop import IOLoop
    import argparse
    from common.config import APIConfig
    import sys
    import os
    import motor
    
    parser = argparse.ArgumentParser()
    parser.add_argument("-c", "--config-file", dest='config_file',
                        help="Config file location")
    args = parser.parse_args()
    CONF = APIConfig().parse(args.config_file)
    
    client = motor.MotorClient(CONF.mongo_url)
    db = client[CONF.mongo_dbname]
    
    class Handler(web.RequestHandler):
    
        executor = ThreadPoolExecutor(10)
    
        def initialize(self):
            """ Prepares the database for the entire class """
            self.db = self.settings["db"]
    
        @gen.coroutine
        def get(self):
            self.blocking_task("Ping", "Void-R")
    
    
        @run_on_executor
        def blocking_task(self, name, status):
            IOLoop.instance().add_callback(callback=lambda: self.some_update(name, status))
    
        @gen.coroutine
        def some_update(self, name, status):
            mongo_dict = yield self.db.test_cases.find_one({"name": name})
            self.db.test_cases.update({ "name": name }, { "$set": { "status" : status } } )
    
    
    if __name__ == "__main__":
        app = web.Application([
            (r"/", Handler),
        ],
            db=db,
            debug=CONF.api_debug_on,
        )
        app.listen(8888)
        IOLoop.current().start()