symbian-qemu-0.9.1-12/python-2.6.1/Demo/threads/Coroutine.py
changeset 1 2fb8b9db1c86
equal deleted inserted replaced
0:ffa851df0825 1:2fb8b9db1c86
       
     1 # Coroutine implementation using Python threads.
       
     2 #
       
     3 # Combines ideas from Guido's Generator module, and from the coroutine
       
     4 # features of Icon and Simula 67.
       
     5 #
       
     6 # To run a collection of functions as coroutines, you need to create
       
     7 # a Coroutine object to control them:
       
     8 #    co = Coroutine()
       
     9 # and then 'create' a subsidiary object for each function in the
       
    10 # collection:
       
    11 #    cof1 = co.create(f1 [, arg1, arg2, ...]) # [] means optional,
       
    12 #    cof2 = co.create(f2 [, arg1, arg2, ...]) #... not list
       
    13 #    cof3 = co.create(f3 [, arg1, arg2, ...])
       
    14 # etc.  The functions need not be distinct; 'create'ing the same
       
    15 # function multiple times gives you independent instances of the
       
    16 # function.
       
    17 #
       
    18 # To start the coroutines running, use co.tran on one of the create'd
       
    19 # functions; e.g., co.tran(cof2).  The routine that first executes
       
    20 # co.tran is called the "main coroutine".  It's special in several
       
    21 # respects:  it existed before you created the Coroutine object; if any of
       
    22 # the create'd coroutines exits (does a return, or suffers an unhandled
       
    23 # exception), EarlyExit error is raised in the main coroutine; and the
       
    24 # co.detach() method transfers control directly to the main coroutine
       
    25 # (you can't use co.tran() for this because the main coroutine doesn't
       
    26 # have a name ...).
       
    27 #
       
    28 # Coroutine objects support these methods:
       
    29 #
       
    30 # handle = .create(func [, arg1, arg2, ...])
       
    31 #    Creates a coroutine for an invocation of func(arg1, arg2, ...),
       
    32 #    and returns a handle ("name") for the coroutine so created.  The
       
    33 #    handle can be used as the target in a subsequent .tran().
       
    34 #
       
    35 # .tran(target, data=None)
       
    36 #    Transfer control to the create'd coroutine "target", optionally
       
    37 #    passing it an arbitrary piece of data. To the coroutine A that does
       
    38 #    the .tran, .tran acts like an ordinary function call:  another
       
    39 #    coroutine B can .tran back to it later, and if it does A's .tran
       
    40 #    returns the 'data' argument passed to B's tran.  E.g.,
       
    41 #
       
    42 #    in coroutine coA   in coroutine coC    in coroutine coB
       
    43 #      x = co.tran(coC)   co.tran(coB)        co.tran(coA,12)
       
    44 #      print x # 12
       
    45 #
       
    46 #    The data-passing feature is taken from Icon, and greatly cuts
       
    47 #    the need to use global variables for inter-coroutine communication.
       
    48 #
       
    49 # .back( data=None )
       
    50 #    The same as .tran(invoker, data=None), where 'invoker' is the
       
    51 #    coroutine that most recently .tran'ed control to the coroutine
       
    52 #    doing the .back.  This is akin to Icon's "&source".
       
    53 #
       
    54 # .detach( data=None )
       
    55 #    The same as .tran(main, data=None), where 'main' is the
       
    56 #    (unnameable!) coroutine that started it all.  'main' has all the
       
    57 #    rights of any other coroutine:  upon receiving control, it can
       
    58 #    .tran to an arbitrary coroutine of its choosing, go .back to
       
    59 #    the .detach'er, or .kill the whole thing.
       
    60 #
       
    61 # .kill()
       
    62 #    Destroy all the coroutines, and return control to the main
       
    63 #    coroutine.  None of the create'ed coroutines can be resumed after a
       
    64 #    .kill().  An EarlyExit exception does a .kill() automatically.  It's
       
    65 #    a good idea to .kill() coroutines you're done with, since the
       
    66 #    current implementation consumes a thread for each coroutine that
       
    67 #    may be resumed.
       
    68 
       
    69 import thread
       
    70 import sync
       
    71 
       
    72 class _CoEvent:
       
    73     def __init__(self, func):
       
    74         self.f = func
       
    75         self.e = sync.event()
       
    76 
       
    77     def __repr__(self):
       
    78         if self.f is None:
       
    79             return 'main coroutine'
       
    80         else:
       
    81             return 'coroutine for func ' + self.f.func_name
       
    82 
       
    83     def __hash__(self):
       
    84         return id(self)
       
    85 
       
    86     def __cmp__(x,y):
       
    87         return cmp(id(x), id(y))
       
    88 
       
    89     def resume(self):
       
    90         self.e.post()
       
    91 
       
    92     def wait(self):
       
    93         self.e.wait()
       
    94         self.e.clear()
       
    95 
       
    96 class Killed(Exception): pass
       
    97 class EarlyExit(Exception): pass
       
    98 
       
    99 class Coroutine:
       
   100     def __init__(self):
       
   101         self.active = self.main = _CoEvent(None)
       
   102         self.invokedby = {self.main: None}
       
   103         self.killed = 0
       
   104         self.value  = None
       
   105         self.terminated_by = None
       
   106 
       
   107     def create(self, func, *args):
       
   108         me = _CoEvent(func)
       
   109         self.invokedby[me] = None
       
   110         thread.start_new_thread(self._start, (me,) + args)
       
   111         return me
       
   112 
       
   113     def _start(self, me, *args):
       
   114         me.wait()
       
   115         if not self.killed:
       
   116             try:
       
   117                 try:
       
   118                     apply(me.f, args)
       
   119                 except Killed:
       
   120                     pass
       
   121             finally:
       
   122                 if not self.killed:
       
   123                     self.terminated_by = me
       
   124                     self.kill()
       
   125 
       
   126     def kill(self):
       
   127         if self.killed:
       
   128             raise TypeError, 'kill() called on dead coroutines'
       
   129         self.killed = 1
       
   130         for coroutine in self.invokedby.keys():
       
   131             coroutine.resume()
       
   132 
       
   133     def back(self, data=None):
       
   134         return self.tran( self.invokedby[self.active], data )
       
   135 
       
   136     def detach(self, data=None):
       
   137         return self.tran( self.main, data )
       
   138 
       
   139     def tran(self, target, data=None):
       
   140         if not self.invokedby.has_key(target):
       
   141             raise TypeError, '.tran target %r is not an active coroutine' % (target,)
       
   142         if self.killed:
       
   143             raise TypeError, '.tran target %r is killed' % (target,)
       
   144         self.value = data
       
   145         me = self.active
       
   146         self.invokedby[target] = me
       
   147         self.active = target
       
   148         target.resume()
       
   149 
       
   150         me.wait()
       
   151         if self.killed:
       
   152             if self.main is not me:
       
   153                 raise Killed
       
   154             if self.terminated_by is not None:
       
   155                 raise EarlyExit, '%r terminated early' % (self.terminated_by,)
       
   156 
       
   157         return self.value
       
   158 
       
   159 # end of module