added BaseTwistedHubm which can be used with twisted's own mainloop (i.e. reactor.run())

This commit is contained in:
Denis Bilenko
2008-10-29 13:28:47 +06:00
parent 1d2d8981d6
commit 6baf93e434

View File

@@ -32,7 +32,71 @@ class socket_rwdescriptor:
return self.logstr
class TwistedHub(object):
class BaseTwistedHub(object):
"""This hub does not run a dedicated greenlet for the mainloop (unlike TwistedHub).
Instead, it assumes that the mainloop is run in the main greenlet.
This makes running "green" functions in the main greenlet impossible but is useful
when you want to run reactor.run() yourself.
"""
def __init__(self, mainloop_greenlet):
self.greenlet = mainloop_greenlet
self.waiters_by_greenlet = {}
def switch(self):
assert greenlet.getcurrent() is not self.greenlet, 'Impossible to switch() from the mainloop greenlet'
try:
greenlet.getcurrent().parent = self.greenlet
except ValueError, ex:
pass
return greenlib.switch(self.greenlet)
def stop(self):
from twisted.internet import reactor
reactor.stop()
def sleep(self, seconds=0):
from twisted.internet import reactor
d = reactor.callLater(seconds, greenlib.switch, greenlet.getcurrent())
self.switch()
def add_descriptor(self, fileno, read=None, write=None, exc=None):
#print 'add_descriptor', fileno, read, write, exc
descriptor = socket_rwdescriptor(fileno, read, write, exc)
from twisted.internet import reactor
if read:
reactor.addReader(descriptor)
if write:
reactor.addWriter(descriptor)
# XXX exc will not work if no read nor write
self.waiters_by_greenlet[greenlet.getcurrent()] = descriptor
return descriptor
def remove_descriptor(self, descriptor):
from twisted.internet import reactor
reactor.removeReader(descriptor)
reactor.removeWriter(descriptor)
self.waiters_by_greenlet.pop(greenlet.getcurrent(), None)
def exc_greenlet(self, gr, exception_object):
fileno = self.waiters_by_greenlet.pop(gr, None)
if fileno is not None:
self.remove_descriptor(fileno)
greenlib.switch(gr, None, exception_object)
# required by GreenSocket
def exc_descriptor(self, _fileno):
pass # XXX do something sensible here
# required by greenlet_body
def cancel_timers(self, greenlet, quiet=False):
pass # XXX do something sensible here
def schedule_call(self, seconds, func, *args, **kwargs):
from twisted.internet import reactor
return reactor.callLater(seconds, func, *args, **kwargs)
class TwistedHub(BaseTwistedHub):
# wrapper around reactor that runs reactor's main loop in a separate greenlet.
# whenever you need to wait, i.e. inside a call that must appear
# blocking, call hub.switch() (then your blocking operation should switch back to you
@@ -53,7 +117,8 @@ class TwistedHub(object):
def __init__(self):
assert Hub.state==0, ('This hub can only be instantiated once', Hub.state)
Hub.state = 1
self.greenlet = None
make_twisted_threadpool_daemonic() # otherwise the program would hang after the main greenlet exited
BaseTwistedHub.__init__(self, None)
def switch(self):
if not self.greenlet:
@@ -110,42 +175,38 @@ class TwistedHub(object):
t = reactor.running and t2
reactor.doIteration(t)
def stop(self):
from twisted.internet import reactor
reactor.stop()
def running_greenlets(self):
res = []
for g in greenlib.tracked_greenlets():
if g is self.greenlet:
continue
if hasattr(self.greenlet, 'parent') and g is self.greenlet.parent:
continue
res.append(g)
def sleep(self, seconds=0):
from twisted.internet import reactor
d = reactor.callLater(seconds, greenlib.switch, greenlet.getcurrent())
self.switch()
def add_descriptor(self, fileno, read=None, write=None, exc=None):
#print 'add_descriptor', fileno, read, write, exc
descriptor = socket_rwdescriptor(fileno, read, write, exc)
from twisted.internet import reactor
if read:
reactor.addReader(descriptor)
if write:
reactor.addWriter(descriptor)
# XXX exc will not work if no read nor write
return descriptor
def remove_descriptor(self, descriptor):
from twisted.internet import reactor
reactor.removeReader(descriptor)
reactor.removeWriter(descriptor)
# required by GreenSocket
def exc_descriptor(self, _fileno):
pass # XXX do something sensible here
# required by greenlet_body
def cancel_timers(self, greenlet, quiet=False):
pass # XXX do something sensible here
def schedule_call(self, seconds, func, *args, **kwargs):
from twisted.internet import reactor
return reactor.callLater(seconds, func, *args, **kwargs)
def join(self, lst, timeout=None):
"""Wait for other greenlets to finish"""
waiting = [1]
# if timeout is not None and self.running_greenlets():
# def stop():
# waiting[0] = 0
# self.schedule_call(timeout, stop)
while True:
print 'WHILE!'
lst = [x for x in lst if not x.dead]
print 'WHILE!', lst
if not lst:
break # XXX collect return values
print 'WHILE! - before switch', lst[0], lst[0].parent
print 'WHILE! - before switch', greenlet.getcurrent(), greenlet.getcurrent().parent
print 'WHILE! - before switch', self.greenlet, getattr(self.greenlet, 'parent', '-')
for x in lst:
x.parent = greenlet.getcurrent()
print 'AWHILE! - before switch', lst[0], lst[0].parent
print 'AWHILE! - before switch', greenlet.getcurrent(), greenlet.getcurrent().parent
print 'AWHILE! - before switch', self.greenlet, getattr(self.greenlet, 'parent', '-')
res = self.switch()
print 'res=%r' % res
Hub = TwistedHub
@@ -158,4 +219,4 @@ def make_twisted_threadpool_daemonic():
if ThreadPool.threadFactory != DaemonicThread:
ThreadPool.threadFactory = DaemonicThread
make_twisted_threadpool_daemonic() # otherwise the program would hang after the main greenlet exited