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