Friday, November 30, 2012

Cancelling Deferreds, part 2

In my last post I talked about the motivation for cancellation, and how one cancels a Deferred. In this post I'm going to explain how to make a Deferred created by your code do something useful when cancelled.

Let's imagine you are implementing an HTTP client, which returns a Deferred firing with the response from the server. Cancellation is best achieved by closing the connection. In order to make cancellation do that, all you have to do is pass a function to the constructor of the Deferred (it will get called with the Deferred that is being cancelled):

class HTTPClient(Protocol):
    def request(self, method, path):
        self.resultDeferred = Deferred(
            lambda ignore: self.transport.abortConnection())
        request = b"%s %s HTTP/1.0\r\n\r\n" % (method, path)
        self.transport.write(request)
        return self.resultDeferred

    def dataReceived(self, data):
        # ... parse HTTP response ...
        # ... eventually call self.resultDeferred.callback() ...
Now if someone calls cancel() on the Deferred returned from HTTPClient.request(), the HTTP request will be cancelled (assuming it's not too late to do so). Care should be taken not to callback() a Deferred that has already been cancelled.

As you can see, making your Deferreds cancellable is fairly simple, and the benefits are great: the ability to cancel an operation half-way in a standard manner, freeing up resources that no longer need to be used. In addition, as we will see in my next post, cancellation can be used as the basis for adding timeouts to Deferreds.

No comments:

Post a Comment