149 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			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_
 |