chiark / gitweb /
Make `tripe' be the default key type.
[tripe] / py / rmcr.py
CommitLineData
2fa80010
MW
1### -*-python-*-
2###
3### Rich man's coroutines
4###
5### (c) 2006 Straylight/Edgeware
6###
7
8###----- Licensing notice ---------------------------------------------------
9###
10### This file is part of Trivial IP Encryption (TrIPE).
11###
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.
16###
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.
21###
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.
25
26__pychecker__ = 'self=me'
27
28###--------------------------------------------------------------------------
29### External dependencies.
30
31import thread as T
32from sys import exc_info
33
34###--------------------------------------------------------------------------
35### What's going on?
36###
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.
40
41###--------------------------------------------------------------------------
42### Low-level machinery.
43
44__debug = False
45def _debug(msg):
46 if __debug:
47 print '+++ %s: %s' % (T.get_ident(), msg)
48
49def _switchto(cr, arg = None, exc = None):
50 """Switch to coroutine CR."""
51 global active
52 _debug('> _switchto(%s, %s, %s)' % (cr, arg, exc))
53 if not cr.livep:
54 raise ValueError, 'coroutine is dead'
55 cr._arg = arg
56 cr._exc = exc
57 if cr is active:
58 _debug(' _switchto: self-switch to %s' % cr)
59 else:
60 _debug(' _switchto: switch from %s to %s' % (active, cr))
61 active = cr
62 _debug(' _switchto: %s releasing' % cr)
63 assert cr._lk.locked()
64 cr._lk.release()
65 _debug('< _switchto')
66
67###--------------------------------------------------------------------------
68### Coroutine object.
69
70def findvictim(cr):
71 """Find an appropriate victim coroutine for something, starting from CR."""
72 while not cr:
73 if cr is None:
74 return main
75 cr = cr.parent
76 return cr
77
78class Coroutine (object):
79 """Heard of lightweight threads? Well, this is a heavyweight coroutine."""
80
81 def __init__(me, func = None, name = None, parent = None, __tid = None):
82 """
83 Create a new coroutine object.
84
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.
90 """
91 global active
92 _debug('> __init__(%s, func = %s, tid = %s)' % (name, func, __tid))
93 me.name = name
94 me._lk = T.allocate_lock()
95 _debug(' __init__: %s locking' % me)
96 me._lk.acquire()
97 me.livep = True
98 me._exc = None
99 me._arg = None
100 me.parent = parent or active
101 me._onexit = [None, None]
102 if __tid is not None:
103 me._tid = __tid
104 _debug(' __init__: manufacture cr %s with existing thread %d' %
105 (me, __tid))
106 me._startp = False
107 else:
108 me._func = func or me.run
109 me._tid = T.start_new_thread(me._start, ())
110 me._startp = True
111 assert me._lk.locked()
112 _debug(' __init__: create %s with new thread %d' % (me, me._tid))
113 _debug('< __init__(%s)' % me)
114
115 def __str__(me):
116 """Stringify a coroutine using its name if possible."""
117 if me.name is not None:
118 return '<Coroutine %s>' % me.name
119 else:
120 return repr(me)
121
122 def _start(me):
123 """
124 Start up the coroutine.
125
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.
128 """
129 _debug('> _start(%s)' % (me))
130 args, kwargs = me._wait()
131 _debug(' start(%s): args = %s, kwargs = %s' % (me, args, kwargs))
132 me._startp = False
133 try:
134 try:
135 _debug(' _start(%s): call user (args = %s, kwargs = %s)' %
136 (me, args, kwargs))
137 me._func(*args, **kwargs)
138 except:
139 _switchto(findvictim(me.parent), None, exc_info())
140 finally:
141 _debug(' _start(%s): finally' % me)
142 _debug(' _start(%s): _onexit = %s' % (me, me._onexit))
143 me.livep = False
144 _switchto(findvictim(me.parent), *me._onexit)
145 _debug('< _start(%s)' % me)
146
147 def _wait(me):
148 """Wait for this coroutine to become active."""
149 global active
150 _debug('> _wait(%s)' % me)
151 me._lk.acquire()
152 while me is not active:
153 _debug(' _wait(%s): locking' % me)
154 me._lk.acquire()
155 _debug(' _wait(%s): active, arg = %s, exc = %s' %
156 (me, me._arg, me._exc))
157 if me._exc:
158 raise me._exc[0], me._exc[1], me._exc[2]
159 _debug('< _wait(%s): %s' % (me, me._arg))
160 return me._arg
161
162 def switch(me, *args, **kwargs):
163 """Switch to this coroutine, passing it the object OBJ."""
164 global active
165 _debug('> switch(%s, args = %s, kwargs = %s)' % (me, args, kwargs))
166 if me._startp:
167 obj = args, kwargs
168 else:
169 obj, = args or (None,)
170 assert not kwargs
171 old = active
172 _switchto(me, obj)
173 _debug('< switch')
174 return old._wait()
175
176 def getcurrent():
177 return active
178 getcurrent = staticmethod(getcurrent)
179
180 def __nonzero__(me):
181 return me.livep
182
183 def throw(me, exc, arg = None, tb = None):
184 """
185 Switch to this coroutine, throwing it an exception.
186
187 The exception is given by EXC, ARG and TB, which form the exception,
188 argument, traceback triple.
189 """
190 global active
191 _debug('> throw(%s, %s, args = %s)' % (me, exc, arg))
192 old = active
193 me._exc = [exc, arg, tb]
194 _switchto(me, None)
195 _debug('< throw')
196 return old._wait()
197
198 def run(me, *args, **kw):
199 raise Exception('Coroutine has no body to run')
200
201###--------------------------------------------------------------------------
202### Setup stuff.
203
204active = None
205main = Coroutine(_Coroutine__tid = T.get_ident(), name = '_main')
206active = main
207
208###----- That's all, folks --------------------------------------------------