[Twisted-Python] How to force synchronous behavior

James Y Knight foom at fuhm.net
Sun Oct 30 21:58:51 EST 2005


Given that there has already been a long thread about how one should  
not do this, which I completely agree with, I'll not repeat it. Note  
that I do not recommend this solution to the OP, but rather post this  
just to appease to the people complaining about how the only answer  
given is "don't do that". "Don't do that" is the correct answer given  
the information so far available, but, here's a different non-answer,  
with code.

On Oct 28, 2005, at 5:40 PM, Pedro Sanchez wrote:
> def mySyncFunc()
>    x = 0
>    def done(data):
>       global x
>       x = data
>
>    d = someCalculation()
>    d.addCallback(done)
>    <something here to hold until "done" is really done>
>    return x
>
> print mySyncFunc()
>

The above code cannot work because for "done" to become done, the  
twisted event loop must get a chance to run. For the event loop to  
get a chance to run, you must return from that function. Basically,  
how can you "block" waiting for an async event, while letting the  
twisted event loop continue running? There is an answer: run your  
code in a separate thread. However, that doesn't work either, because  
the OP wants to use twisted APIs like ADBAPI or networking in  
someCalculation. So there really is no way to do literally what's  
asked in a working way. But, you can get something "like" the above  
by splitting your code into synch-like-code to be run in a separate  
thread, and async-twisted-using-code to be run in the twisted reactor  
thread.

Here's a little example. Please note, again, that I do not recommend  
doing this except in circumstances where you absolutely must.

import Queue
from twisted.internet import reactor, defer
from twisted.python import failure

def callInReactor(__f, *__a, **__kw):
     # Called in other thread
     queue = Queue.Queue()
     reactor.callFromThread(_calledFromThread, queue, __f, __a, __kw)
     result = queue.get()
     if isinstance(result, failure.Failure):
         result.raiseException()
     return result

def _calledFromThread(queue, f, a, kw):
     # Called in reactor thread
     result = defer.maybeDeferred(f, *a, **kw)
     result.addBoth(queue.put)

def someCalculation():
   # A demo "calculation"
   d = defer.Deferred()
   reactor.callLater(4, d.callback, 'hi')
   return d

x = 0
def myAsyncFunctionInTwistedThread():
   def done(data):
     global x
     x = data
   d = someCalculation()
   d.addCallback(done)
   return d

def mySyncFuncInAnotherThread():
   # NOTE: in this other thread you cannot call any twisted APIs  
besides reactor.callFromThread
   # and a very few select others.
   callInReactor(myAsyncFunctionInTwistedThread)
   # callInReactor blocks until the deferred returned by  
myAsyncFunctionInTwistedThread fires
   print x

# Start up a thread to call your blocking function in
reactor.callInThread(mySyncFuncInAnotherThread)

# Run the reactor
reactor.run()




James




More information about the Twisted-Python mailing list