chiark / gitweb /
Disassociate public key tags from peer names.
[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
6a2ebadb 32from sys import exc_info, excepthook
2fa80010
MW
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))
6a2ebadb 137 me._onexit = [me._func(*args, **kwargs), None]
2fa80010 138 except:
e41b17c8
MW
139 exc = exc_info()
140 _debug(' _start(%s): caught exception (%s)' % (me, exc))
6a2ebadb 141 me._onexit = [None, exc]
2fa80010
MW
142 finally:
143 _debug(' _start(%s): finally' % me)
144 _debug(' _start(%s): _onexit = %s' % (me, me._onexit))
145 me.livep = False
146 _switchto(findvictim(me.parent), *me._onexit)
147 _debug('< _start(%s)' % me)
148
149 def _wait(me):
150 """Wait for this coroutine to become active."""
151 global active
152 _debug('> _wait(%s)' % me)
153 me._lk.acquire()
154 while me is not active:
155 _debug(' _wait(%s): locking' % me)
156 me._lk.acquire()
157 _debug(' _wait(%s): active, arg = %s, exc = %s' %
158 (me, me._arg, me._exc))
159 if me._exc:
160 raise me._exc[0], me._exc[1], me._exc[2]
161 _debug('< _wait(%s): %s' % (me, me._arg))
162 return me._arg
163
164 def switch(me, *args, **kwargs):
165 """Switch to this coroutine, passing it the object OBJ."""
166 global active
167 _debug('> switch(%s, args = %s, kwargs = %s)' % (me, args, kwargs))
168 if me._startp:
169 obj = args, kwargs
170 else:
171 obj, = args or (None,)
172 assert not kwargs
173 old = active
174 _switchto(me, obj)
175 _debug('< switch')
176 return old._wait()
177
178 def getcurrent():
179 return active
180 getcurrent = staticmethod(getcurrent)
181
182 def __nonzero__(me):
183 return me.livep
184
185 def throw(me, exc, arg = None, tb = None):
186 """
187 Switch to this coroutine, throwing it an exception.
188
189 The exception is given by EXC, ARG and TB, which form the exception,
190 argument, traceback triple.
191 """
192 global active
193 _debug('> throw(%s, %s, args = %s)' % (me, exc, arg))
194 old = active
195 me._exc = [exc, arg, tb]
196 _switchto(me, None)
197 _debug('< throw')
198 return old._wait()
199
200 def run(me, *args, **kw):
201 raise Exception('Coroutine has no body to run')
202
203###--------------------------------------------------------------------------
204### Setup stuff.
205
206active = None
207main = Coroutine(_Coroutine__tid = T.get_ident(), name = '_main')
208active = main
209
210###----- That's all, folks --------------------------------------------------