diff options
Diffstat (limited to 'Demo/threads/Coroutine.py')
-rw-r--r-- | Demo/threads/Coroutine.py | 159 |
1 files changed, 0 insertions, 159 deletions
diff --git a/Demo/threads/Coroutine.py b/Demo/threads/Coroutine.py deleted file mode 100644 index 690fadc..0000000 --- a/Demo/threads/Coroutine.py +++ /dev/null @@ -1,159 +0,0 @@ -# Coroutine implementation using Python threads. -# -# Combines ideas from Guido's Generator module, and from the coroutine -# features of Icon and Simula 67. -# -# To run a collection of functions as coroutines, you need to create -# a Coroutine object to control them: -# co = Coroutine() -# and then 'create' a subsidiary object for each function in the -# collection: -# cof1 = co.create(f1 [, arg1, arg2, ...]) # [] means optional, -# cof2 = co.create(f2 [, arg1, arg2, ...]) #... not list -# cof3 = co.create(f3 [, arg1, arg2, ...]) -# etc. The functions need not be distinct; 'create'ing the same -# function multiple times gives you independent instances of the -# function. -# -# To start the coroutines running, use co.tran on one of the create'd -# functions; e.g., co.tran(cof2). The routine that first executes -# co.tran is called the "main coroutine". It's special in several -# respects: it existed before you created the Coroutine object; if any of -# the create'd coroutines exits (does a return, or suffers an unhandled -# exception), EarlyExit error is raised in the main coroutine; and the -# co.detach() method transfers control directly to the main coroutine -# (you can't use co.tran() for this because the main coroutine doesn't -# have a name ...). -# -# Coroutine objects support these methods: -# -# handle = .create(func [, arg1, arg2, ...]) -# Creates a coroutine for an invocation of func(arg1, arg2, ...), -# and returns a handle ("name") for the coroutine so created. The -# handle can be used as the target in a subsequent .tran(). -# -# .tran(target, data=None) -# Transfer control to the create'd coroutine "target", optionally -# passing it an arbitrary piece of data. To the coroutine A that does -# the .tran, .tran acts like an ordinary function call: another -# coroutine B can .tran back to it later, and if it does A's .tran -# returns the 'data' argument passed to B's tran. E.g., -# -# in coroutine coA in coroutine coC in coroutine coB -# x = co.tran(coC) co.tran(coB) co.tran(coA,12) -# print x # 12 -# -# The data-passing feature is taken from Icon, and greatly cuts -# the need to use global variables for inter-coroutine communication. -# -# .back( data=None ) -# The same as .tran(invoker, data=None), where 'invoker' is the -# coroutine that most recently .tran'ed control to the coroutine -# doing the .back. This is akin to Icon's "&source". -# -# .detach( data=None ) -# The same as .tran(main, data=None), where 'main' is the -# (unnameable!) coroutine that started it all. 'main' has all the -# rights of any other coroutine: upon receiving control, it can -# .tran to an arbitrary coroutine of its choosing, go .back to -# the .detach'er, or .kill the whole thing. -# -# .kill() -# Destroy all the coroutines, and return control to the main -# coroutine. None of the create'ed coroutines can be resumed after a -# .kill(). An EarlyExit exception does a .kill() automatically. It's -# a good idea to .kill() coroutines you're done with, since the -# current implementation consumes a thread for each coroutine that -# may be resumed. - -import _thread as thread -import sync - -class _CoEvent: - def __init__(self, func): - self.f = func - self.e = sync.event() - - def __repr__(self): - if self.f is None: - return 'main coroutine' - else: - return 'coroutine for func ' + self.f.__name__ - - def __hash__(self): - return id(self) - - def __cmp__(x,y): - return cmp(id(x), id(y)) - - def resume(self): - self.e.post() - - def wait(self): - self.e.wait() - self.e.clear() - -class Killed(Exception): pass -class EarlyExit(Exception): pass - -class Coroutine: - def __init__(self): - self.active = self.main = _CoEvent(None) - self.invokedby = {self.main: None} - self.killed = 0 - self.value = None - self.terminated_by = None - - def create(self, func, *args): - me = _CoEvent(func) - self.invokedby[me] = None - thread.start_new_thread(self._start, (me,) + args) - return me - - def _start(self, me, *args): - me.wait() - if not self.killed: - try: - try: - me.f(*args) - except Killed: - pass - finally: - if not self.killed: - self.terminated_by = me - self.kill() - - def kill(self): - if self.killed: - raise TypeError('kill() called on dead coroutines') - self.killed = 1 - for coroutine in self.invokedby.keys(): - coroutine.resume() - - def back(self, data=None): - return self.tran( self.invokedby[self.active], data ) - - def detach(self, data=None): - return self.tran( self.main, data ) - - def tran(self, target, data=None): - if target not in self.invokedby: - raise TypeError('.tran target %r is not an active coroutine' % (target,)) - if self.killed: - raise TypeError('.tran target %r is killed' % (target,)) - self.value = data - me = self.active - self.invokedby[target] = me - self.active = target - target.resume() - - me.wait() - if self.killed: - if self.main is not me: - raise Killed - if self.terminated_by is not None: - raise EarlyExit('%r terminated early' % (self.terminated_by,)) - - return self.value - -# end of module |