Using Tornado in a blocking fashion

604 views
Skip to first unread message

Berk Birand

unread,
Aug 26, 2014, 3:36:42 AM8/26/14
to python-...@googlegroups.com
Hi all,

I'm working on a project for which I need to run background network tasks in the background. Since I want to build this on top of IPython, I figured I could tap into the Tornado ioloop that is already used in the IPython Notebook. Yet, I can't seem to find the right way of using the @gen.coroutine calls.

Effectively, what I would like to do is to block on a future. I know I can do this using the `yield` keyword as follows:

@gen.coroutine
def f():
    #..
    msg = yield recv_future() #this returns a Future object
    raise gen.Return(msg)

But the issue is that the function calling this must also use the `yield` syntax, becoming a generator on its own. What I think I need is a way of blocking the current "thread-like" execution on a Future (while the ioloop is still active):

def f():
    msg = recv_future().result() #Blocking call
    return msg

This is available in the regular coroutine.futures library (https://docs.python.org/dev/library/concurrent.futures.html#concurrent.futures.Future.result). But when I run use it from within Tornado, I get an exception saying that DummyFuture doesn't support this.

How can I get this "blocking" feature with the Tornado ioloop? I have the coroutine package installed, but still get the "DummyFuture"  exception.

PS: I created a GitHub issue for this (https://github.com/tornadoweb/tornado/issues/1161), but since this is not really a problem with Tornado, but rather with my understanding of it, I'm also posting this. Thanks to @ajdavis for helping me out there.

Yuan Wang

unread,
Aug 26, 2014, 11:37:10 PM8/26/14
to python-...@googlegroups.com
Using 'msg = recv_future().result()'  can not block the function. You should always use the 'yield'.

@gen.coroutine
def f():
    #..
    msg = yield recv_future() #this returns a Future object
    print msg #this will be executed after get result
    raise gen.Return(msg)

if __name__ == '__main__':

     f()
     ioloop.start()



--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornad...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
-----------------------------------------------------------------------------------------------------
王 远
QQ  :807228688
 

Dustin Spicuzza

unread,
Aug 28, 2014, 3:31:31 PM8/28/14
to python-...@googlegroups.com
You can use IOLoop.run_sync() to block on a future. 

Berk Birand

unread,
Aug 31, 2014, 2:01:43 PM8/31/14
to python-...@googlegroups.com
But doesn't that close the IOLoop after the future is completed? That's
the impression I got from the description in the documentation.

Namely, if I have some callbacks scheduled on the IOLoop, and then run
the run_sync command, would the callbacks still be scheduled when
run_sync returns?
> <https://docs.python.org/dev/library/concurrent.futures.html#concurrent.futures.Future.result>).
> But when I run use it from within Tornado, I get an exception saying
> that DummyFuture doesn't support this.
>
> How can I get this "blocking" feature with the Tornado ioloop? I
> have the coroutine package installed, but still get the
> "DummyFuture" exception.
>
> PS: I created a GitHub issue for this
> (https://github.com/tornadoweb/tornado/issues/1161
> <https://github.com/tornadoweb/tornado/issues/1161>), but since this
> is not really a problem with Tornado, but rather with my
> understanding of it, I'm also posting this. Thanks to @ajdavis for
> helping me out there.
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Tornado Web Server" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/python-tornado/eU5Dhs5xHvE/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> python-tornad...@googlegroups.com
> <mailto:python-tornad...@googlegroups.com>.

Dustin Spicuzza

unread,
Aug 31, 2014, 2:13:08 PM8/31/14
to python-...@googlegroups.com

Ah, you're right, I didn't read your post carefully enough. You're
right, you can only call run_sync when the ioloop isn't running.

I have other thoughts, I'll post them on the bug report you referenced.

Dustin

Ben Darnell

unread,
Aug 31, 2014, 2:52:12 PM8/31/14
to Tornado Mailing List
On Sun, Aug 31, 2014 at 2:01 PM, Berk Birand <berkb...@gmail.com> wrote:
But doesn't that close the IOLoop after the future is completed? That's the impression I got from the description in the documentation.

The IOLoop is stopped, not closed, when the run_sync future completes.  This means that you could restart it, with either start() or another run_sync() call.  However, in most cases when run_sync is appropriate it is used with a new IOLoop that is only used once.
 

Namely, if I have some callbacks scheduled on the IOLoop, and then run the run_sync command, would the callbacks still be scheduled when run_sync returns?

They're still scheduled, but will not be run unless and until the IOLoop is restarted.


To return to your original question, there are a couple of reasons Tornado's Futures do not support blocking in the result() method.

First, in a single-threaded application (the common case for Tornado), a blocking result() method would simply deadlock.  Control cannot return to the IOLoop until the result() call finishes, but it will never finish because whatever you're waiting on will not be able to run either.  The IOLoop must be allowed to advance for anything to make progress.  (At this point it is tempting to make the IOLoop reentrant by allowing you to restart it from within a callback.  This turns out to be a bad idea for the same reasons that reentrant mutexes are a bad idea, among others)

Secondly, in a multi-threaded application, it does make some sense to allow one thread to block on a Future that is being completed on another thread.  Things used to work this way in Tornado 3.x (as long as the concurrent.futures package was available).  However, the amount of extra locking required to make this work incurs a substantial performance penalty and offers no benefit in the common (single-threaded) case.  

One way to get this blocking functionality back would be to implement the concurrent.futures.Executor interface for the IOLoop.  This could cover the thread-safety issues on both directions.  Untested sketch:

  class IOLoopExecutor(concurrent.futures.Executor):
    def __init__(self, io_loop):
      self.io_loop = io_loop

    def submit(self, fn, *args, **kwargs):
       # runs in the caller's thread
       future = concurrent.futures.Future()
       self.io_loop.add_callback(self._run_callback(future, fn, args, kwargs))
       return future

    def _run_callback(self, concurrent_future, fn, args, kwargs):
      # runs in the IOLoop's thread
      tornado_future = fn(*args, **kwargs))
      tornado.concurrent.chain_future(tornado_future, concurrent_future)

-Ben



Dustin Spicuzza wrote:

For more options, visit https://groups.google.com/d/optout.

--
You received this message because you are subscribed to the Google Groups "Tornado Web Server" group.
To unsubscribe from this group and stop receiving emails from it, send an email to python-tornado+unsubscribe@googlegroups.com.

李小万

unread,
Dec 13, 2014, 8:35:02 PM12/13/14
to python-...@googlegroups.com
Hello , my case is like with you  , hao do you solve?

在 2014年8月26日星期二UTC+8下午3时36分42秒,Berk Birand写道:

Berk Birand

unread,
Dec 15, 2014, 5:15:36 PM12/15/14
to python-...@googlegroups.com
I am using threads that run in the background instead of Tornado.
> <https://docs.python.org/dev/library/concurrent.futures.html#concurrent.futures.Future.result>).
> But when I run use it from within Tornado, I get an exception saying
> that DummyFuture doesn't support this.
>
> How can I get this "blocking" feature with the Tornado ioloop? I
> have the coroutine package installed, but still get the
> "DummyFuture" exception.
>
> PS: I created a GitHub issue for this
> (https://github.com/tornadoweb/tornado/issues/1161
> <https://github.com/tornadoweb/tornado/issues/1161>), but since this
> is not really a problem with Tornado, but rather with my
> understanding of it, I'm also posting this. Thanks to @ajdavis for
> helping me out there.
>
> --
> You received this message because you are subscribed to a topic in the
> Google Groups "Tornado Web Server" group.
> To unsubscribe from this topic, visit
> https://groups.google.com/d/topic/python-tornado/eU5Dhs5xHvE/unsubscribe.
> To unsubscribe from this group and all its topics, send an email to
> python-tornad...@googlegroups.com
> <mailto:python-tornad...@googlegroups.com>.
Reply all
Reply to author
Forward
0 new messages