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 it under
13 ### the terms of the GNU General Public License as published by the Free
14 ### Software Foundation; either version 3 of the License, or (at your
15 ### option) any later version.
17 ### TrIPE is distributed in the hope that it will be useful, but WITHOUT
18 ### ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 ### FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 ### You should have received a copy of the GNU General Public License
23 ### along with TrIPE. If not, see <https://www.gnu.org/licenses/>.
25 __pychecker__ = 'self=me'
27 ###--------------------------------------------------------------------------
28 ### External dependencies.
31 from sys import exc_info, excepthook
33 ###--------------------------------------------------------------------------
36 ### A coroutine is just a thread. Each coroutine has a recursive lock
37 ### associated with it. The lock is almost always held. In order to switch
38 ### to a coroutine, one releases its lock and then (re)locks one's own.
40 ###--------------------------------------------------------------------------
41 ### Low-level machinery.
46 print '+++ %s: %s' % (T.get_ident(), msg)
48 def _switchto(cr, arg = None, exc = None):
49 """Switch to coroutine CR."""
51 _debug('> _switchto(%s, %s, %s)' % (cr, arg, exc))
53 raise ValueError, 'coroutine is dead'
57 _debug(' _switchto: self-switch to %s' % cr)
59 _debug(' _switchto: switch from %s to %s' % (active, cr))
61 _debug(' _switchto: %s releasing' % cr)
62 assert cr._lk.locked()
66 ###--------------------------------------------------------------------------
70 """Find an appropriate victim coroutine for something, starting from CR."""
77 class Coroutine (object):
78 """Heard of lightweight threads? Well, this is a heavyweight coroutine."""
80 def __init__(me, func = None, name = None, parent = None, __tid = None):
82 Create a new coroutine object.
84 The new coroutine is immediately activated. FUNC is the function to run,
85 and defaults to the coroutine object's `run' method, so subclassing is a
86 reasonable thing to do; NAME is a friendly name for the coroutine, and
87 shows up in debug traces. The __TID argument is used internally for
88 special effects, and shouldn't be used by external callers.
91 _debug('> __init__(%s, func = %s, tid = %s)' % (name, func, __tid))
93 me._lk = T.allocate_lock()
94 _debug(' __init__: %s locking' % me)
99 me.parent = parent or active
100 me._onexit = [None, None]
101 if __tid is not None:
103 _debug(' __init__: manufacture cr %s with existing thread %d' %
107 me._func = func or me.run
108 me._tid = T.start_new_thread(me._start, ())
110 assert me._lk.locked()
111 _debug(' __init__: create %s with new thread %d' % (me, me._tid))
112 _debug('< __init__(%s)' % me)
115 """Stringify a coroutine using its name if possible."""
116 if me.name is not None:
117 return '<Coroutine %s>' % me.name
123 Start up the coroutine.
125 Wait for this coroutine to become active, and then run the user's
126 function. When (if) that returns, mark the coroutine as dead.
128 _debug('> _start(%s)' % (me))
129 args, kwargs = me._wait()
130 _debug(' start(%s): args = %s, kwargs = %s' % (me, args, kwargs))
134 _debug(' _start(%s): call user (args = %s, kwargs = %s)' %
136 me._onexit = [me._func(*args, **kwargs), None]
139 _debug(' _start(%s): caught exception (%s)' % (me, exc))
140 me._onexit = [None, exc]
142 _debug(' _start(%s): finally' % me)
143 _debug(' _start(%s): _onexit = %s' % (me, me._onexit))
145 _switchto(findvictim(me.parent), *me._onexit)
146 _debug('< _start(%s)' % me)
149 """Wait for this coroutine to become active."""
151 _debug('> _wait(%s)' % me)
153 while me is not active:
154 _debug(' _wait(%s): locking' % me)
156 _debug(' _wait(%s): active, arg = %s, exc = %s' %
157 (me, me._arg, me._exc))
159 raise me._exc[0], me._exc[1], me._exc[2]
160 _debug('< _wait(%s): %s' % (me, me._arg))
163 def switch(me, *args, **kwargs):
164 """Switch to this coroutine, passing it the object OBJ."""
166 _debug('> switch(%s, args = %s, kwargs = %s)' % (me, args, kwargs))
170 obj, = args or (None,)
179 getcurrent = staticmethod(getcurrent)
184 def throw(me, exc, arg = None, tb = None):
186 Switch to this coroutine, throwing it an exception.
188 The exception is given by EXC, ARG and TB, which form the exception,
189 argument, traceback triple.
192 _debug('> throw(%s, %s, args = %s)' % (me, exc, arg))
194 me._exc = [exc, arg, tb]
199 def run(me, *args, **kw):
200 raise Exception('Coroutine has no body to run')
202 ###--------------------------------------------------------------------------
206 main = Coroutine(_Coroutine__tid = T.get_ident(), name = '_main')
209 ###----- That's all, folks --------------------------------------------------