test_ok1/py/c-extension/greenlet/dummy_greenlet.py

149 lines
4.5 KiB
Python

import thread, sys
__all__ = ['greenlet', 'main', 'getcurrent']
class greenlet(object):
__slots__ = ('run', '_controller')
def __init__(self, run=None, parent=None):
if run is not None:
self.run = run
if parent is not None:
self.parent = parent
def switch(self, *args):
global _passaround_
_passaround_ = None, args, None
self._controller.switch(self)
exc, val, tb = _passaround_
del _passaround_
if exc is None:
if isinstance(val, tuple) and len(val) == 1:
return val[0]
else:
return val
else:
raise exc, val, tb
def __nonzero__(self):
return self._controller.isactive()
def __new__(cls, *args, **kwds):
self = object.__new__(cls)
self._controller = _Controller()
return self
def __del__(self):
#print 'DEL:', self
if self._controller.parent is None:
return # don't kill the main greenlet
while self._controller.isactive():
self._controller.kill(self)
def getparent(self):
return self._controller.parent
def setparent(self, nparent):
if not isinstance(nparent, greenlet):
raise TypeError, "parent must be a greenlet"
p = nparent
while p is not None:
if p is self:
raise ValueError, "cyclic parent chain"
p = p._controller.parent
self._controller.parent = nparent
parent = property(getparent, setparent)
del getparent
del setparent
class _Controller:
# Controllers are separated from greenlets to allow greenlets to be
# deallocated while running, when their last reference goes away.
# Care is taken to keep only references to controllers in thread's
# frames' local variables.
# _Controller.parent: the parent greenlet.
# _Controller.lock: the lock used for synchronization
# it is not set before the greenlet starts
# it is None after the greenlet stops
def __init__(self):
self.parent = _current_
def isactive(self):
return getattr(self, 'lock', None) is not None
def switch(self, target):
previous = _current_._controller
self.switch_no_wait(target)
# wait until someone releases this thread's lock
previous.lock.acquire()
def switch_no_wait(self, target):
# lock tricks: each greenlet has its own lock which is almost always
# in 'acquired' state:
# * the current greenlet runs with its lock acquired
# * all other greenlets wait on their own lock's acquire() call
global _current_
try:
while 1:
_current_ = target
lock = self.lock
if lock is not None:
break
target = self.parent
self = target._controller
except AttributeError:
# start the new greenlet
lock = self.lock = thread.allocate_lock()
lock.acquire()
thread.start_new_thread(self.run_thread, (target.run,))
else:
# release (re-enable) the target greenlet's thread
lock.release()
def run_thread(self, run):
#print 'ENTERING', self
global _passaround_
exc, val, tb = _passaround_
if exc is None:
try:
result = run(*val)
except SystemExit, e:
_passaround_ = None, (e,), None
except:
_passaround_ = sys.exc_info()
else:
_passaround_ = None, (result,), None
self.lock = None
#print 'LEAVING', self
self.switch_no_wait(self.parent)
def kill(self, target):
# see comments in greenlet.c:green_dealloc()
global _passaround_
self._parent_ = _current_
_passaround_ = SystemExit, None, None
self.switch(target)
exc, val, tb = _passaround_
del _passaround_
if exc is not None:
if val is None:
print >> sys.stderr, "Exception", "%s" % (exc,),
else:
print >> sys.stderr, "Exception", "%s: %s" % (exc, val),
print >> sys.stderr, "in", self, "ignored"
_current_ = None
main = greenlet()
main._controller.lock = thread.allocate_lock()
main._controller.lock.acquire()
_current_ = main
def getcurrent():
return _current_