Search code examples
twisted

Should Twisted timeouts be cancelled when operation finishes successfully?


What is a recommended practice for handling a timeout that was set for a Twisted operation that finished successfully before the timeout expired?

Should the timeout be left running and depend on the Deferred.cancel() method having no effect on a finished Deferred?

Or is it better to explicitly cancel the timeout when the operation finishes?

The second option seems cleaner, but requires more complex and error-prone code (it is easy to leave a running timer, especially for operations that require several steps).


Solution

  • Maybe the best thing would be to add a timeout-cancelling callback to the deferred in the same place where you set the timeout.

    from twisted.internet import reactor, task
    
    def foo():
        return 'done'
    
    def somethingPossiblyCanceled():
        return task.deferLater(reactor, 1, foo)
    
    def addTimeout(d, duration):
        timeout = reactor.callLater(duration, d.cancel)
        def cancelTimeout(result):
            if timeout.active():
                timeout.cancel()
                print('(timeout canceled)')
            return result
        d.addBoth(cancelTimeout)
    
    def main():
        d = somethingPossiblyCanceled()
    
        addTimeout(d, 2)
    
        def finished(result):
            print(result)
            reactor.stop()
        def canceled(failure):
            print('timed out: {0}'.format(failure))
            reactor.stop()
        d.addCallbacks(finished, canceled)
    
    reactor.callWhenRunning(main)
    reactor.run()