3 ### Rich man's coroutines
5 ### (c) 2006 Straylight/Edgeware
8 ###----- Licensing notice ---------------------------------------------------
10 ### This file is part of Trivial IP Encryption (TrIPE).
12 ### TrIPE is free software; you can redistribute it and/or modify
13 ### it under the terms of the GNU General Public License as published by
14 ### the Free Software Foundation; either version 2 of the License, or
15 ### (at your option) any later version.
17 ### TrIPE is distributed in the hope that it will be useful,
18 ### but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 ### GNU General Public License for more details.
22 ### You should have received a copy of the GNU General Public License
23 ### along with TrIPE; if not, write to the Free Software Foundation,
24 ### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 __pychecker__ = 'self=me'
28 ###--------------------------------------------------------------------------
29 ### External dependencies.
32 from sys import exc_info
34 ###--------------------------------------------------------------------------
37 ### A coroutine is just a thread. Each coroutine has a recursive lock
38 ### associated with it. The lock is almost always held. In order to switch
39 ### to a coroutine, one releases its lock and then (re)locks one's own.
41 ###--------------------------------------------------------------------------
42 ### Low-level machinery.
47 print '+++ %s: %s' % (T.get_ident(), msg)
49 def _switchto(cr, arg = None, exc = None):
50 """Switch to coroutine CR."""
52 _debug('> _switchto(%s, %s, %s)' % (cr, arg, exc))
54 raise ValueError, 'coroutine is dead'
58 _debug(' _switchto: self-switch to %s' % cr)
60 _debug(' _switchto: switch from %s to %s' % (active, cr))
62 _debug(' _switchto: %s releasing' % cr)
63 assert cr._lk.locked()
67 ###--------------------------------------------------------------------------
71 """Find an appropriate victim coroutine for something, starting from CR."""
78 class Coroutine (object):
79 """Heard of lightweight threads? Well, this is a heavyweight coroutine."""
81 def __init__(me, func = None, name = None, parent = None, __tid = None):
83 Create a new coroutine object.
85 The new coroutine is immediately activated. FUNC is the function to run,
86 and defaults to the coroutine object's `run' method, so subclassing is a
87 reasonable thing to do; NAME is a friendly name for the coroutine, and
88 shows up in debug traces. The __TID argument is used internally for
89 special effects, and shouldn't be used by external callers.
92 _debug('> __init__(%s, func = %s, tid = %s)' % (name, func, __tid))
94 me._lk = T.allocate_lock()
95 _debug(' __init__: %s locking' % me)
100 me.parent = parent or active
101 me._onexit = [None, None]
102 if __tid is not None:
104 _debug(' __init__: manufacture cr %s with existing thread %d' %
108 me._func = func or me.run
109 me._tid = T.start_new_thread(me._start, ())
111 assert me._lk.locked()
112 _debug(' __init__: create %s with new thread %d' % (me, me._tid))
113 _debug('< __init__(%s)' % me)
116 """Stringify a coroutine using its name if possible."""
117 if me.name is not None:
118 return '<Coroutine %s>' % me.name
124 Start up the coroutine.
126 Wait for this coroutine to become active, and then run the user's
127 function. When (if) that returns, mark the coroutine as dead.
129 _debug('> _start(%s)' % (me))
130 args, kwargs = me._wait()
131 _debug(' start(%s): args = %s, kwargs = %s' % (me, args, kwargs))
135 _debug(' _start(%s): call user (args = %s, kwargs = %s)' %
137 me._func(*args, **kwargs)
139 _switchto(findvictim(me.parent), None, exc_info())
141 _debug(' _start(%s): finally' % me)
142 _debug(' _start(%s): _onexit = %s' % (me, me._onexit))
144 _switchto(findvictim(me.parent), *me._onexit)
145 _debug('< _start(%s)' % me)
148 """Wait for this coroutine to become active."""
150 _debug('> _wait(%s)' % me)
152 while me is not active:
153 _debug(' _wait(%s): locking' % me)
155 _debug(' _wait(%s): active, arg = %s, exc = %s' %
156 (me, me._arg, me._exc))
158 raise me._exc[0], me._exc[1], me._exc[2]
159 _debug('< _wait(%s): %s' % (me, me._arg))
162 def switch(me, *args, **kwargs):
163 """Switch to this coroutine, passing it the object OBJ."""
165 _debug('> switch(%s, args = %s, kwargs = %s)' % (me, args, kwargs))
169 obj, = args or (None,)
178 getcurrent = staticmethod(getcurrent)
183 def throw(me, exc, arg = None, tb = None):
185 Switch to this coroutine, throwing it an exception.
187 The exception is given by EXC, ARG and TB, which form the exception,
188 argument, traceback triple.
191 _debug('> throw(%s, %s, args = %s)' % (me, exc, arg))
193 me._exc = [exc, arg, tb]
198 def run(me, *args, **kw):
199 raise Exception('Coroutine has no body to run')
201 ###--------------------------------------------------------------------------
205 main = Coroutine(_Coroutine__tid = T.get_ident(), name = '_main')
208 ###----- That's all, folks --------------------------------------------------