[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