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

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? Best Regards Maciej Wasilak

On 07:53 pm, wasilak@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

Jean-Paul, thank you very much for your answer - I would have missed this problem for sure. Second option seems a bit more challenging, I'll try this one. Best Regards Maciej Wasilak 2012/8/29 <exarkun@twistedmatrix.com>
On 07:53 pm, wasilak@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
_______________________________________________ Twisted-Python mailing list Twisted-Python@twistedmatrix.com http://twistedmatrix.com/cgi-bin/mailman/listinfo/twisted-python
participants (2)
-
exarkun@twistedmatrix.com
-
Maciej Wasilak