mirror of
https://github.com/python/cpython.git
synced 2025-07-19 17:25:54 +00:00
Add 1994 Coroutine module by Tim Peters
This commit is contained in:
parent
704925c774
commit
f4e13a4563
4 changed files with 332 additions and 0 deletions
160
Demo/threads/Coroutine.py
Normal file
160
Demo/threads/Coroutine.py
Normal file
|
@ -0,0 +1,160 @@
|
|||
# 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
|
||||
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.func_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()
|
||||
|
||||
Killed = 'Coroutine.Killed'
|
||||
EarlyExit = 'Coroutine.EarlyExit'
|
||||
|
||||
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:
|
||||
apply(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 not self.invokedby.has_key(target):
|
||||
raise TypeError, '.tran target ' + `target` + \
|
||||
' is not an active coroutine'
|
||||
if self.killed:
|
||||
raise TypeError, '.tran target ' + `target` + ' is killed'
|
||||
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, `self.terminated_by` + ' terminated early'
|
||||
|
||||
return self.value
|
||||
|
||||
# end of module
|
Loading…
Add table
Add a link
Reference in a new issue