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)
140 _debug(' _start(%s): caught exception (%s)' % (me, exc))
141 _switchto(findvictim(me.parent), None, exc)
143 _debug(' _start(%s): finally' % me)
144 _debug(' _start(%s): _onexit = %s' % (me, me._onexit))
146 _switchto(findvictim(me.parent), *me._onexit)
147 _debug('< _start(%s)' % me)
150 """Wait for this coroutine to become active."""
152 _debug('> _wait(%s)' % me)
154 while me is not active:
155 _debug(' _wait(%s): locking' % me)
157 _debug(' _wait(%s): active, arg = %s, exc = %s' %
158 (me, me._arg, me._exc))
160 raise me._exc[0], me._exc[1], me._exc[2]
161 _debug('< _wait(%s): %s' % (me, me._arg))
164 def switch(me, *args, **kwargs):
165 """Switch to this coroutine, passing it the object OBJ."""
167 _debug('> switch(%s, args = %s, kwargs = %s)' % (me, args, kwargs))
171 obj, = args or (None,)
180 getcurrent = staticmethod(getcurrent)
185 def throw(me, exc, arg = None, tb = None):
187 Switch to this coroutine, throwing it an exception.
189 The exception is given by EXC, ARG and TB, which form the exception,
190 argument, traceback triple.
193 _debug('> throw(%s, %s, args = %s)' % (me, exc, arg))
195 me._exc = [exc, arg, tb]
200 def run(me, *args, **kw):
201 raise Exception('Coroutine has no body to run')
203 ###--------------------------------------------------------------------------
207 main = Coroutine(_Coroutine__tid = T.get_ident(), name = '_main')
210 ###----- That's all, folks --------------------------------------------------