[Twisted-Python] Using Deferred.called to distinguish between synchronous and asynchronous result

exarkun at twistedmatrix.com exarkun at twistedmatrix.com
Wed Aug 29 16:52:15 EDT 2012


On 07:53 pm, wasilak at gmail.com wrote:
>Hello,
>
>I am implementing UDP-based protocol in Twisted (CoAP) which allows two
>behaviors when answering requests:
>
>1. If response is immediately available - send acknowledgement and 
>response
>in a single datagram (piggyback response)
>2. If response needs to be fetched or prepared - send datagram with
>acknowledgement, and then send another datagram with a response 
>(separate
>response)
>
>(I think behavior #1 is called synchronous in most Twisted tutorials, 
>and
>behavior #2 is called asynchronous.)
>
>When programmer is implementing his application on top of CoAP 
>protocol, he
>or she needs to choose how his request handler is going to behave. I 
>would
>like to handle both behaviors in the same manner - by forcing every
>user-written request handler to return Deferred. Then I would
>check Deferred.called parameter.
>1. If True - callback will execute immediately and send proper 
>ACK+Response
>(that means request handler used defer.succeed() or somethin similar)
>2. If False I send empty ACK, and wait for callback to send Response
>
>code:
>def respond(request):
>        d = requestHandler(request)
>        if d.called is False:
>            sendEmptyAck()
>        d.addCallback(sendResponse)
>
>I assume that sendResponse can send either ACK+RSP, or only RSP.
>
>I would like to ask if this is a proper approach?

This isn't the right way to go.  The `called` attribute is set to `True` 
as soon as `Deferred.callback` is called.  This might sound like it's 
what you want, but only if you disregard the chaining feature of 
Deferreds, where a callback on the Deferred might return a *new* unfired 
Deferred.  Now your original Deferred has `called` set to `True` but you 
don't actually have a result yet.

Instead, there are two obvious options:

  1. Allow the application to return a non-Deferred result.  Use 
`isinstance` to detect this case and do the synchronous send when you 
see a non-Deferred come back.  Send the empty ACK and ultimately the 
result in the other case.

  2. Change your empty ACK logic to be time-based instead.  Say, if the 
application doesn't produce a result within 10 milliseconds, send the 
empty ACK.  Implement this using `reactor.callLater` and 
`IDelayedCall.cancel`.  You'll set up a delayed call to send the empty 
ACK every time you call application code, but in the callback on the 
application's Deferred, you'll cancel that call (unless it has already 
happened).

Hope this helps,
Jean-Paul



More information about the Twisted-Python mailing list