Having
class CnxAwareSite(server.Site):
def __init__(self, *args, **kwargs):
server.Site.__init__(self, *args, **kwargs)
self.cnx_cnt = 0
def buildProtocol(self, addr):
channel = server.Site.buildProtocol(self, addr)
def cntCnxMade(f):
self.cnx_cnt += 1
print 'new conn', self.cnx_cnt
return f
channel.connectionMade = cntCnxMade(channel.connectionMade)
def cntCnxLost(f):
def post_wrap(result):
self.cnx_cnt -= 1
return result
return lambda *args, **kwargs: post_wrap(f(*args, **kwargs))
channel.connectionLost = cntCnxLost(channel.connectionLost)
return channel
...
site = CnxAwareSite(root)
site.port = reactor.listenTCP(PORT, site)
and having installed a trigger, performing async-looping checks for site.cnx_cnt deferred-chained to a site.port.stopListening()
reactor.addSystemEventTrigger('before', 'shutdown', shutdown, site)
is it the right way to ensure the 'clean shutdown' of the service before stopping the reactor?
Using twistd is not an option for me... (sigh)
In general, you've got basically the right idea. Your shutdown
function (which is not shown, so I can't be sure about it) can return a Deferred
that fires when any necessary cleanup is complete. However, I always make the relevant code into an IService
, and then do reactor.addSystemEventTrigger('before', 'shutdown', myService.stopService)
; this makes it easier to test and it makes it possible to integrate your service into a twistd
plugin service hierarchy later, if that possibility becomes available to you.
Specifically, however, you don't need to do this just to stop listening on a port. The reactor will automatically handle that for you at shutdown time. Also: what do you mean by "performing async-looping checks"? If you're just waiting for the connection count to get to zero, then you should just fire a Deferred
when it gets to zero, you shouldn't be looping.
(More detail in your code sample would be useful to address some of these issues.)