symbian-qemu-0.9.1-12/python-2.6.1/Demo/threads/Coroutine.py
changeset 1 2fb8b9db1c86
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/symbian-qemu-0.9.1-12/python-2.6.1/Demo/threads/Coroutine.py	Fri Jul 31 15:01:17 2009 +0100
@@ -0,0 +1,159 @@
+# 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()
+
+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:
+                    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 %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