2000-11-08 11:17:49 -04:00
|
|
|
# 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.
|
|
|
|
|
2008-05-25 10:05:15 -03:00
|
|
|
import _thread as thread
|
2000-11-08 11:17:49 -04:00
|
|
|
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:
|
2007-07-17 17:59:35 -03:00
|
|
|
return 'coroutine for func ' + self.f.__name__
|
2000-11-08 11:17:49 -04:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
Merged revisions 66394,66404,66412,66414,66424-66436 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk
........
r66394 | benjamin.peterson | 2008-09-11 17:04:02 -0500 (Thu, 11 Sep 2008) | 1 line
fix typo
........
r66404 | gerhard.haering | 2008-09-12 08:54:06 -0500 (Fri, 12 Sep 2008) | 2 lines
sqlite3 module: Mark iterdump() method as "Non-standard" like all the other methods not found in DB-API.
........
r66412 | gerhard.haering | 2008-09-12 13:58:57 -0500 (Fri, 12 Sep 2008) | 2 lines
Fixes issue #3103. In the sqlite3 module, made one more function static. All renaming public symbos now have the pysqlite prefix to avoid name clashes. This at least once created problems where the same symbol name appeared somewhere in Apache and the sqlite3 module was used from mod_python.
........
r66414 | gerhard.haering | 2008-09-12 17:33:22 -0500 (Fri, 12 Sep 2008) | 2 lines
Issue #3846: Release GIL during calls to sqlite3_prepare. This improves concurrent access to the same database file from multiple threads/processes.
........
r66424 | andrew.kuchling | 2008-09-12 20:22:08 -0500 (Fri, 12 Sep 2008) | 1 line
#687648 from Robert Schuppenies: use classic division. (RM Barry gave permission to update the demos.)
........
r66425 | andrew.kuchling | 2008-09-12 20:27:33 -0500 (Fri, 12 Sep 2008) | 1 line
#687648 from Robert Schuppenies: use classic division. From me: don't use string exception; flush stdout after printing
........
r66426 | andrew.kuchling | 2008-09-12 20:34:41 -0500 (Fri, 12 Sep 2008) | 1 line
#687648 from Robert Schuppenies: use classic division. From me: don't use string exception; add __main__ section
........
r66427 | andrew.kuchling | 2008-09-12 20:42:55 -0500 (Fri, 12 Sep 2008) | 1 line
#687648 from Robert Schuppenies: use classic division. From me: remove two stray semicolons
........
r66428 | andrew.kuchling | 2008-09-12 20:43:28 -0500 (Fri, 12 Sep 2008) | 1 line
#687648 from Robert Schuppenies: use classic division.
........
r66429 | andrew.kuchling | 2008-09-12 20:47:02 -0500 (Fri, 12 Sep 2008) | 1 line
Remove semicolon
........
r66430 | andrew.kuchling | 2008-09-12 20:48:36 -0500 (Fri, 12 Sep 2008) | 1 line
Subclass exception
........
r66431 | andrew.kuchling | 2008-09-12 20:56:56 -0500 (Fri, 12 Sep 2008) | 1 line
Fix SyntaxError
........
r66432 | andrew.kuchling | 2008-09-12 20:57:25 -0500 (Fri, 12 Sep 2008) | 1 line
Update uses of string exceptions
........
r66433 | andrew.kuchling | 2008-09-12 21:08:30 -0500 (Fri, 12 Sep 2008) | 1 line
Use title case
........
r66434 | andrew.kuchling | 2008-09-12 21:09:15 -0500 (Fri, 12 Sep 2008) | 1 line
Remove extra 'the'; the following title includes it
........
r66435 | andrew.kuchling | 2008-09-12 21:11:51 -0500 (Fri, 12 Sep 2008) | 1 line
#3288: Document as_integer_ratio
........
r66436 | andrew.kuchling | 2008-09-12 21:14:15 -0500 (Fri, 12 Sep 2008) | 1 line
Use title case
........
2008-09-13 12:58:53 -03:00
|
|
|
class Killed(Exception): pass
|
|
|
|
class EarlyExit(Exception): pass
|
2000-11-08 11:17:49 -04:00
|
|
|
|
|
|
|
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:
|
2006-03-17 04:00:19 -04:00
|
|
|
me.f(*args)
|
2000-11-08 11:17:49 -04:00
|
|
|
except Killed:
|
|
|
|
pass
|
|
|
|
finally:
|
|
|
|
if not self.killed:
|
|
|
|
self.terminated_by = me
|
|
|
|
self.kill()
|
|
|
|
|
|
|
|
def kill(self):
|
|
|
|
if self.killed:
|
2007-07-17 17:59:35 -03:00
|
|
|
raise TypeError('kill() called on dead coroutines')
|
2000-11-08 11:17:49 -04:00
|
|
|
self.killed = 1
|
2007-08-06 18:07:53 -03:00
|
|
|
for coroutine in self.invokedby.keys():
|
2000-11-08 11:17:49 -04:00
|
|
|
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):
|
2007-07-17 17:59:35 -03:00
|
|
|
if target not in self.invokedby:
|
|
|
|
raise TypeError('.tran target %r is not an active coroutine' % (target,))
|
2000-11-08 11:17:49 -04:00
|
|
|
if self.killed:
|
2007-07-17 17:59:35 -03:00
|
|
|
raise TypeError('.tran target %r is killed' % (target,))
|
2000-11-08 11:17:49 -04:00
|
|
|
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:
|
2007-07-17 17:59:35 -03:00
|
|
|
raise EarlyExit('%r terminated early' % (self.terminated_by,))
|
2000-11-08 11:17:49 -04:00
|
|
|
|
|
|
|
return self.value
|
|
|
|
|
|
|
|
# end of module
|