id	summary	reporter	owner	description	type	status	priority	milestone	component	resolution	keywords	cc	branch	branch_author	launchpad_bug
5485	Canceling a pending TCP4ClientEndpoint connection leads to an AlreadyCalledError	jssebastian		"It seems that if a tcp connection is hanging (e.g. because the destiantion host sends no reply), attempting to cancel it will lead to a spurious AlreadyCalledError.

Here is a small test case that triggers this bug:


{{{
from twisted.internet import reactor
from twisted.internet.protocol import Factory, Protocol
from twisted.internet.endpoints import TCP4ClientEndpoint
from twisted.internet import defer

class Greeter(Protocol):
    pass

class GreeterFactory(Factory):
    def buildProtocol(self, addr):
        return Greeter()

def gotProtocol(p):
    print ""Got protocol""
    
def connectionFailed(f):
    print ""Connection failed""

def timeout(d):
    print ""Got timeout""
    if not d.called:
        d.cancel()
        

#defer.setDebugging(True)
point = TCP4ClientEndpoint(reactor, ""www.google.com"", 666)
d = point.connect(GreeterFactory())
d.addCallback(gotProtocol)
d.addErrback(connectionFailed)
reactor.callLater(10, timeout, d)
reactor.run()

}}}

Notice I even test d.called before calling d.cancel().
www.google.com does not respond on port 666, and after 10 seconds I get:


{{{
$python alreadycalled.py
Got timeout
Connection failed
Unhandled Error
Traceback (most recent call last):
  File ""alreadycalled.py"", line 28, in <module>
    reactor.run()
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/base.py"", line 1169, in run
    self.mainLoop()
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/base.py"", line 1178, in mainLoop
    self.runUntilCurrent()
--- <exception caught here> ---
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/base.py"", line 800, in runUntilCurrent
    call.func(*call.args, **call.kw)
  File ""alreadycalled.py"", line 21, in timeout
    d.cancel()
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/defer.py"", line 427, in cancel
    canceller(self)
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/endpoints.py"", line 261, in _canceller
    error.ConnectingCancelledError(connector.getDestination()))
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/defer.py"", line 391, in errback
    self._startRunCallbacks(fail)
  File ""/usr/local/lib/python2.6/dist-packages/Twisted-11.1.0-py2.6-linux-x86_64.egg/twisted/internet/defer.py"", line 451, in _startRunCallbacks
    raise AlreadyCalledError
twisted.internet.defer.AlreadyCalledError: 

}}}

Notice that the connection failed message is printed *after* the cancel() call.

Tested on: 

twisted 11.1.0, python 2.6 on ubuntu 10.04 64 bit 

twisted 11.1.0, python 2.7 on ubuntu 11.10 64 bit



"	defect	closed	normal		core	duplicate	endpoints cancel				
