diff --git a/py/__init__.py b/py/__init__.py index fffcdab61..a727e40f0 100644 --- a/py/__init__.py +++ b/py/__init__.py @@ -6,7 +6,6 @@ interacting with filesystems. - `py.test`_: cross-project testing tool with many advanced features - `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes -- `py.magic.greenlet`_: micro-threads on standard CPython ("stackless-light") and PyPy - `py.path`_: path abstractions over local and subversion files - `py.code`_: dynamic code compile and traceback printing support @@ -16,19 +15,18 @@ http://pylib.org/contact.html .. _`py.test`: http://pylib.org/test.html .. _`py.execnet`: http://pylib.org/execnet.html -.. _`py.magic.greenlet`: http://pylib.org/greenlet.html .. _`py.path`: http://pylib.org/path.html .. _`py.code`: http://pylib.org/code.html """ from initpkg import initpkg -version = "1.0.0a7" +version = "1.0.0a8" initpkg(__name__, description = "pylib and py.test: agile development and test support library", - revision = int('$LastChangedRevision: 63319 $'.split(':')[1][:-1]), - lastchangedate = '$LastChangedDate: 2009-03-25 12:50:57 +0100 (Wed, 25 Mar 2009) $', + revision = int('$LastChangedRevision: 63340 $'.split(':')[1][:-1]), + lastchangedate = '$LastChangedDate: 2009-03-26 10:33:50 +0100 (Thu, 26 Mar 2009) $', version = version, url = "http://pylib.org", download_url = "http://codespeak.net/py/%s/download.html" % version, diff --git a/py/c-extension/__init__.py b/py/c-extension/__init__.py deleted file mode 100644 index 792d60054..000000000 --- a/py/c-extension/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# diff --git a/py/c-extension/greenlet/README.txt b/py/c-extension/greenlet/README.txt deleted file mode 100644 index c4e03ccde..000000000 --- a/py/c-extension/greenlet/README.txt +++ /dev/null @@ -1,13 +0,0 @@ - -This code is by Armin Rigo with the core pieces by Christian Tismer. -These pieces are: - -- slp_platformselect.h -- switch_*.h, included from the previous one. - -All additional credits for the general idea of stack switching also go to -Christian. In other words, if it works it is thanks to Christian's work, -and if it crashes it is because of a bug of mine :-) - - --- Armin diff --git a/py/c-extension/greenlet/dummy_greenlet.py b/py/c-extension/greenlet/dummy_greenlet.py deleted file mode 100644 index 8aab7e087..000000000 --- a/py/c-extension/greenlet/dummy_greenlet.py +++ /dev/null @@ -1,148 +0,0 @@ -import thread, sys - -__all__ = ['greenlet', 'main', 'getcurrent'] - - -class greenlet(object): - __slots__ = ('run', '_controller') - - def __init__(self, run=None, parent=None): - if run is not None: - self.run = run - if parent is not None: - self.parent = parent - - def switch(self, *args): - global _passaround_ - _passaround_ = None, args, None - self._controller.switch(self) - exc, val, tb = _passaround_ - del _passaround_ - if exc is None: - if isinstance(val, tuple) and len(val) == 1: - return val[0] - else: - return val - else: - raise exc, val, tb - - def __nonzero__(self): - return self._controller.isactive() - - def __new__(cls, *args, **kwds): - self = object.__new__(cls) - self._controller = _Controller() - return self - - def __del__(self): - #print 'DEL:', self - if self._controller.parent is None: - return # don't kill the main greenlet - while self._controller.isactive(): - self._controller.kill(self) - - def getparent(self): - return self._controller.parent - - def setparent(self, nparent): - if not isinstance(nparent, greenlet): - raise TypeError, "parent must be a greenlet" - p = nparent - while p is not None: - if p is self: - raise ValueError, "cyclic parent chain" - p = p._controller.parent - self._controller.parent = nparent - - parent = property(getparent, setparent) - del getparent - del setparent - - -class _Controller: - # Controllers are separated from greenlets to allow greenlets to be - # deallocated while running, when their last reference goes away. - # Care is taken to keep only references to controllers in thread's - # frames' local variables. - - # _Controller.parent: the parent greenlet. - # _Controller.lock: the lock used for synchronization - # it is not set before the greenlet starts - # it is None after the greenlet stops - - def __init__(self): - self.parent = _current_ - - def isactive(self): - return getattr(self, 'lock', None) is not None - - def switch(self, target): - previous = _current_._controller - self.switch_no_wait(target) - # wait until someone releases this thread's lock - previous.lock.acquire() - - def switch_no_wait(self, target): - # lock tricks: each greenlet has its own lock which is almost always - # in 'acquired' state: - # * the current greenlet runs with its lock acquired - # * all other greenlets wait on their own lock's acquire() call - global _current_ - try: - while 1: - _current_ = target - lock = self.lock - if lock is not None: - break - target = self.parent - self = target._controller - except AttributeError: - # start the new greenlet - lock = self.lock = thread.allocate_lock() - lock.acquire() - thread.start_new_thread(self.run_thread, (target.run,)) - else: - # release (re-enable) the target greenlet's thread - lock.release() - - def run_thread(self, run): - #print 'ENTERING', self - global _passaround_ - exc, val, tb = _passaround_ - if exc is None: - try: - result = run(*val) - except SystemExit, e: - _passaround_ = None, (e,), None - except: - _passaround_ = sys.exc_info() - else: - _passaround_ = None, (result,), None - self.lock = None - #print 'LEAVING', self - self.switch_no_wait(self.parent) - - def kill(self, target): - # see comments in greenlet.c:green_dealloc() - global _passaround_ - self._parent_ = _current_ - _passaround_ = SystemExit, None, None - self.switch(target) - exc, val, tb = _passaround_ - del _passaround_ - if exc is not None: - if val is None: - print >> sys.stderr, "Exception", "%s" % (exc,), - else: - print >> sys.stderr, "Exception", "%s: %s" % (exc, val), - print >> sys.stderr, "in", self, "ignored" - - -_current_ = None -main = greenlet() -main._controller.lock = thread.allocate_lock() -main._controller.lock.acquire() -_current_ = main - -def getcurrent(): - return _current_ diff --git a/py/c-extension/greenlet/greenlet.c b/py/c-extension/greenlet/greenlet.c deleted file mode 100644 index 016b15d22..000000000 --- a/py/c-extension/greenlet/greenlet.c +++ /dev/null @@ -1,981 +0,0 @@ -#include "greenlet.h" -#include "structmember.h" - - -/* XXX major open bugs: - XXX - no GC. Unfinished greenlets won't be deallocated if they - XXX contain a cycle to themselves from anywhere in their frame stack. -*/ - -/*********************************************************** - -A PyGreenlet is a range of C stack addresses that must be -saved and restored in such a way that the full range of the -stack contains valid data when we switch to it. - -Stack layout for a greenlet: - - | ^^^ | - | older data | - | | - stack_stop . |_______________| - . | | - . | greenlet data | - . | in stack | - . * |_______________| . . _____________ stack_copy + stack_saved - . | | | | - . | data | |greenlet data| - . | unrelated | | saved | - . | to | | in heap | - stack_start . | this | . . |_____________| stack_copy - | greenlet | - | | - | newer data | - | vvv | - - -Note that a greenlet's stack data is typically partly at its correct -place in the stack, and partly saved away in the heap, but always in -the above configuration: two blocks, the more recent one in the heap -and the older one still in the stack (either block may be empty). - -Greenlets are chained: each points to the previous greenlet, which is -the one that owns the data currently in the C stack above my -stack_stop. The currently running greenlet is the first element of -this chain. The main (initial) greenlet is the last one. Greenlets -whose stack is entirely in the heap can be skipped from the chain. - -The chain is not related to execution order, but only to the order -in which bits of C stack happen to belong to greenlets at a particular -point in time. - -The main greenlet doesn't have a stack_stop: it is responsible for the -complete rest of the C stack, and we don't know where it begins. We -use (char*) -1, the largest possible address. - -States: - stack_stop == NULL && stack_start == NULL: did not start yet - stack_stop != NULL && stack_start == NULL: already finished - stack_stop != NULL && stack_start != NULL: active - -The running greenlet's stack_start is undefined but not NULL. - - ***********************************************************/ - -/*** global state ***/ - -/* In the presence of multithreading, this is a bit tricky: - - - ts_current always store a reference to a greenlet, but it is - not really the current greenlet after a thread switch occurred. - - - each *running* greenlet uses its run_info field to know which - thread it is attached to. A greenlet can only run in the thread - where it was created. This run_info is a ref to tstate->dict. - - - the thread state dict is used to save and restore ts_current, - using the dictionary key 'ts_curkey'. -*/ - -static PyGreenlet* ts_current; -static PyGreenlet* ts_origin; -static PyGreenlet* ts_target; -static PyObject* ts_passaround; - -/***********************************************************/ -/* Thread-aware routines, switching global variables when needed */ - -#define STATE_OK (ts_current->run_info == PyThreadState_GET()->dict \ - || !green_updatecurrent()) - -static PyObject* ts_curkey; -static PyObject* ts_delkey; -static PyObject* PyExc_GreenletError; -static PyObject* PyExc_GreenletExit; - -static PyGreenlet* green_create_main(void) -{ - PyGreenlet* gmain; - PyObject* dict = PyThreadState_GetDict(); - if (dict == NULL) { - if (!PyErr_Occurred()) - PyErr_NoMemory(); - return NULL; - } - - /* create the main greenlet for this thread */ - gmain = (PyGreenlet*) PyType_GenericAlloc(&PyGreen_Type, 0); - if (gmain == NULL) - return NULL; - gmain->stack_start = (char*) 1; - gmain->stack_stop = (char*) -1; - gmain->run_info = dict; - Py_INCREF(dict); - return gmain; -} - -static int green_updatecurrent(void) -{ - PyThreadState* tstate; - PyGreenlet* next; - PyGreenlet* previous; - PyObject* deleteme; - - /* save ts_current as the current greenlet of its own thread */ - previous = ts_current; - if (PyDict_SetItem(previous->run_info, ts_curkey, (PyObject*) previous)) - return -1; - - /* get ts_current from the active tstate */ - tstate = PyThreadState_GET(); - if (tstate->dict && (next = - (PyGreenlet*) PyDict_GetItem(tstate->dict, ts_curkey))) { - /* found -- remove it, to avoid keeping a ref */ - Py_INCREF(next); - if (PyDict_SetItem(tstate->dict, ts_curkey, Py_None)) - PyErr_Clear(); - } - else { - /* first time we see this tstate */ - next = green_create_main(); - if (next == NULL) - return -1; - } - ts_current = next; - Py_DECREF(previous); - /* green_dealloc() cannot delete greenlets from other threads, so - it stores them in the thread dict; delete them now. */ - deleteme = PyDict_GetItem(tstate->dict, ts_delkey); - if (deleteme != NULL) { - PyList_SetSlice(deleteme, 0, INT_MAX, NULL); - } - return 0; -} - -static PyObject* green_statedict(PyGreenlet* g) -{ - while (!PyGreen_STARTED(g)) - g = g->parent; - return g->run_info; -} - -/***********************************************************/ - -static int g_save(PyGreenlet* g, char* stop) -{ - /* Save more of g's stack into the heap -- at least up to 'stop' - - g->stack_stop |________| - | | - | __ stop . . . . . - | | ==> . . - |________| _______ - | | | | - | | | | - g->stack_start | | |_______| g->stack_copy - - */ - long sz1 = g->stack_saved; - long sz2 = stop - g->stack_start; - assert(g->stack_start != NULL); - if (sz2 > sz1) { - char* c = PyMem_Realloc(g->stack_copy, sz2); - if (!c) { - PyErr_NoMemory(); - return -1; - } - memcpy(c+sz1, g->stack_start+sz1, sz2-sz1); - g->stack_copy = c; - g->stack_saved = sz2; - } - return 0; -} - -static void slp_restore_state(void) -{ - PyGreenlet* g = ts_target; - - /* Restore the heap copy back into the C stack */ - if (g->stack_saved != 0) { - memcpy(g->stack_start, g->stack_copy, g->stack_saved); - PyMem_Free(g->stack_copy); - g->stack_copy = NULL; - g->stack_saved = 0; - } - if (ts_current->stack_stop == g->stack_stop) - g->stack_prev = ts_current->stack_prev; - else - g->stack_prev = ts_current; -} - -static int slp_save_state(char* stackref) -{ - /* must free all the C stack up to target_stop */ - char* target_stop = ts_target->stack_stop; - assert(ts_current->stack_saved == 0); - if (ts_current->stack_start == NULL) - ts_current = ts_current->stack_prev; /* not saved if dying */ - else - ts_current->stack_start = stackref; - - while (ts_current->stack_stop < target_stop) { - /* ts_current is entierely within the area to free */ - if (g_save(ts_current, ts_current->stack_stop)) - return -1; /* XXX */ - ts_current = ts_current->stack_prev; - } - if (ts_current != ts_target) { - if (g_save(ts_current, target_stop)) - return -1; /* XXX */ - } - return 0; -} - - -/* - * the following macros are spliced into the OS/compiler - * specific code, in order to simplify maintenance. - */ - -#define SLP_SAVE_STATE(stackref, stsizediff) \ - stackref += STACK_MAGIC; \ - if (slp_save_state((char*)stackref)) return -1; \ - if (!PyGreen_ACTIVE(ts_target)) return 1; \ - stsizediff = ts_target->stack_start - (char*)stackref - -#define SLP_RESTORE_STATE() \ - slp_restore_state() - - -#define SLP_EVAL -#include "slp_platformselect.h" - -#ifndef STACK_MAGIC -#error "greenlet needs to be ported to this platform,\ - or teached how to detect your compiler properly." -#endif - - -/* This is a trick to prevent the compiler from inlining or - removing the frames */ -int (*_PyGreen_slp_switch) (void); -int (*_PyGreen_switchstack) (void); -void (*_PyGreen_initialstub) (void*); - -static int g_switchstack(void) -{ - /* perform a stack switch according to some global variables - that must be set before: - - ts_current: current greenlet (holds a reference) - - ts_target: greenlet to switch to - - ts_passaround: NULL if PyErr_Occurred(), - else a tuple of args sent to ts_target (holds a reference) - */ - int err; - { /* save state */ - PyThreadState* tstate = PyThreadState_GET(); - ts_current->recursion_depth = tstate->recursion_depth; - ts_current->top_frame = tstate->frame; - } - ts_origin = ts_current; - err = _PyGreen_slp_switch(); - if (err < 0) { /* error */ - Py_XDECREF(ts_passaround); - ts_passaround = NULL; - } - else { - PyThreadState* tstate = PyThreadState_GET(); - tstate->recursion_depth = ts_target->recursion_depth; - tstate->frame = ts_target->top_frame; - ts_target->top_frame = NULL; - ts_current = ts_target; - Py_INCREF(ts_target); - Py_DECREF(ts_origin); - } - return err; -} - -static PyObject* g_switch(PyGreenlet* target, PyObject* args) -{ - /* _consumes_ a reference to the args tuple, - and return a new tuple reference */ - - /* check ts_current */ - if (!STATE_OK) { - Py_DECREF(args); - return NULL; - } - if (green_statedict(target) != ts_current->run_info) { - PyErr_SetString(PyExc_GreenletError, - "cannot switch to a different thread"); - Py_DECREF(args); - return NULL; - } - ts_passaround = args; - - /* find the real target by ignoring dead greenlets, - and if necessary starting a greenlet. */ - while (1) { - if (PyGreen_ACTIVE(target)) { - ts_target = target; - _PyGreen_switchstack(); - return ts_passaround; - } - if (!PyGreen_STARTED(target)) { - void* dummymarker; - ts_target = target; - _PyGreen_initialstub(&dummymarker); - return ts_passaround; - } - target = target->parent; - } -} - -static PyObject *g_handle_exit(PyObject *result) -{ - if (result == NULL && - PyErr_ExceptionMatches(PyExc_GreenletExit)) { - /* catch and ignore GreenletExit */ - PyObject *exc, *val, *tb; - PyErr_Fetch(&exc, &val, &tb); - if (val == NULL) { - Py_INCREF(Py_None); - val = Py_None; - } - result = val; - Py_DECREF(exc); - Py_XDECREF(tb); - } - if (result != NULL) { - /* package the result into a 1-tuple */ - PyObject* r = result; - result = PyTuple_New(1); - if (result) - PyTuple_SET_ITEM(result, 0, r); - else - Py_DECREF(r); - } - return result; -} - -static void g_initialstub(void* mark) -{ - int err; - PyObject* o; - - /* ts_target.run is the object to call in the new greenlet */ - PyObject* run = PyObject_GetAttrString((PyObject*) ts_target, "run"); - if (run == NULL) { - Py_XDECREF(ts_passaround); - ts_passaround = NULL; - return; - } - /* now use run_info to store the statedict */ - o = ts_target->run_info; - ts_target->run_info = green_statedict(ts_target->parent); - Py_INCREF(ts_target->run_info); - Py_XDECREF(o); - - /* start the greenlet */ - ts_target->stack_start = NULL; - ts_target->stack_stop = (char*) mark; - if (ts_current->stack_start == NULL) /* ts_current is dying */ - ts_target->stack_prev = ts_current->stack_prev; - else - ts_target->stack_prev = ts_current; - ts_target->top_frame = NULL; - ts_target->recursion_depth = PyThreadState_GET()->recursion_depth; - err = _PyGreen_switchstack(); - /* returns twice! - The 1st time with err=1: we are in the new greenlet - The 2nd time with err=0: back in the caller's greenlet - */ - if (err == 1) { - /* in the new greenlet */ - PyObject* args; - PyObject* result; - PyGreenlet* ts_self = ts_current; - ts_self->stack_start = (char*) 1; /* running */ - - args = ts_passaround; - if (args == NULL) /* pending exception */ - result = NULL; - else { - /* call g.run(*args) */ - result = PyEval_CallObject(run, args); - Py_DECREF(args); - } - Py_DECREF(run); - result = g_handle_exit(result); - /* jump back to parent */ - ts_self->stack_start = NULL; /* dead */ - g_switch(ts_self->parent, result); - /* must not return from here! */ - PyErr_WriteUnraisable((PyObject*) ts_self); - Py_FatalError("greenlets cannot continue"); - } - /* back in the parent */ -} - - -/***********************************************************/ - - -static PyObject* green_new(PyTypeObject *type, PyObject *args, PyObject *kwds) -{ - PyObject* o; - if (!STATE_OK) - return NULL; - - o = type->tp_alloc(type, 0); - if (o != NULL) { - Py_INCREF(ts_current); - ((PyGreenlet*) o)->parent = ts_current; - } - return o; -} - -static int green_setrun(PyGreenlet* self, PyObject* nparent, void* c); -static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c); - -static int green_init(PyGreenlet *self, PyObject *args, PyObject *kwds) -{ - PyObject *run = NULL; - PyObject* nparent = NULL; - static char *kwlist[] = {"run", "parent", 0}; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OO:green", kwlist, - &run, &nparent)) - return -1; - - if (run != NULL) { - if (green_setrun(self, run, NULL)) - return -1; - } - if (nparent != NULL) - return green_setparent(self, nparent, NULL); - return 0; -} - -static int kill_greenlet(PyGreenlet* self) -{ - /* Cannot raise an exception to kill the greenlet if - it is not running in the same thread! */ - if (self->run_info == PyThreadState_GET()->dict) { - /* The dying greenlet cannot be a parent of ts_current - because the 'parent' field chain would hold a - reference */ - PyObject* result; - if (!STATE_OK) - return -1; - Py_INCREF(ts_current); - self->parent = ts_current; - /* Send the greenlet a GreenletExit exception. */ - PyErr_SetNone(PyExc_GreenletExit); - result = g_switch(self, NULL); - if (result == NULL) - return -1; - Py_DECREF(result); - return 0; - } - else { - /* Not the same thread! Temporarily save the greenlet - into its thread's ts_delkey list. */ - PyObject* lst; - lst = PyDict_GetItem(self->run_info, ts_delkey); - if (lst == NULL) { - lst = PyList_New(0); - if (lst == NULL || PyDict_SetItem(self->run_info, - ts_delkey, lst) < 0) - return -1; - } - if (PyList_Append(lst, (PyObject*) self) < 0) - return -1; - if (!STATE_OK) /* to force ts_delkey to be reconsidered */ - return -1; - return 0; - } -} - -static void green_dealloc(PyGreenlet* self) -{ - PyObject *error_type, *error_value, *error_traceback; - - Py_XDECREF(self->parent); - self->parent = NULL; - if (PyGreen_ACTIVE(self)) { - /* Hacks hacks hacks copied from instance_dealloc() */ - /* Temporarily resurrect the greenlet. */ - assert(self->ob_refcnt == 0); - self->ob_refcnt = 1; - /* Save the current exception, if any. */ - PyErr_Fetch(&error_type, &error_value, &error_traceback); - if (kill_greenlet(self) < 0) { - PyErr_WriteUnraisable((PyObject*) self); - /* XXX what else should we do? */ - } - /* Restore the saved exception. */ - PyErr_Restore(error_type, error_value, error_traceback); - /* Undo the temporary resurrection; can't use DECREF here, - * it would cause a recursive call. - */ - assert(self->ob_refcnt > 0); - --self->ob_refcnt; - if (self->ob_refcnt == 0 && PyGreen_ACTIVE(self)) { - /* Not resurrected, but still not dead! - XXX what else should we do? we complain. */ - PyObject* f = PySys_GetObject("stderr"); - if (f != NULL) { - PyFile_WriteString("GreenletExit did not kill ", - f); - PyFile_WriteObject((PyObject*) self, f, 0); - PyFile_WriteString("\n", f); - } - Py_INCREF(self); /* leak! */ - } - if (self->ob_refcnt != 0) { - /* Resurrected! */ - int refcnt = self->ob_refcnt; - _Py_NewReference((PyObject*) self); - self->ob_refcnt = refcnt; -#ifdef COUNT_ALLOCS - --self->ob_type->tp_frees; - --self->ob_type->tp_allocs; -#endif - return; - } - } - if (self->weakreflist != NULL) - PyObject_ClearWeakRefs((PyObject *) self); - Py_XDECREF(self->run_info); - self->ob_type->tp_free((PyObject*) self); -} - -static PyObject* single_result(PyObject* results) -{ - if (results != NULL && PyTuple_Check(results) && - PyTuple_GET_SIZE(results) == 1) { - PyObject *result = PyTuple_GET_ITEM(results, 0); - Py_INCREF(result); - Py_DECREF(results); - return result; - } - else - return results; -} - -static PyObject * -throw_greenlet(PyGreenlet* self, PyObject* typ, PyObject* val, PyObject* tb) -{ - /* Note: _consumes_ a reference to typ, val, tb */ - PyObject *result = NULL; - PyErr_Restore(typ, val, tb); - if (PyGreen_STARTED(self) && !PyGreen_ACTIVE(self)) { - /* dead greenlet: turn GreenletExit into a regular return */ - result = g_handle_exit(result); - } - return single_result(g_switch(self, result)); -} - -PyDoc_STRVAR(switch_doc, -"switch([val]) -> switch execution to greenlet optionally passing a value, " -"return value passed when switching back"); - -static PyObject* green_switch(PyGreenlet* self, PyObject* args) -{ - Py_INCREF(args); - return single_result(g_switch(self, args)); -} - -#ifndef PyExceptionClass_Check /* Python < 2.5 */ -# define PyExceptionClass_Check PyClass_Check -#endif -#ifndef PyExceptionInstance_Check /* Python < 2.5 */ -# define PyExceptionInstance_Check PyInstance_Check -#endif -#ifndef PyExceptionInstance_Class /* Python < 2.5 */ -# define PyExceptionInstance_Class(x) \ - ((PyObject*)((PyInstanceObject*)(x))->in_class) -#endif - -PyDoc_STRVAR(throw_doc, -"throw(typ[,val[,tb]]) -> raise exception in greenlet, return value passed " -"when switching back"); - -static PyObject* green_throw(PyGreenlet* self, PyObject* args) -{ - PyObject *typ = PyExc_GreenletExit; - PyObject *val = NULL; - PyObject *tb = NULL; - - if (!PyArg_ParseTuple(args, "|OOO:throw", &typ, &val, &tb)) - return NULL; - - /* First, check the traceback argument, replacing None with - NULL. */ - if (tb == Py_None) - tb = NULL; - else if (tb != NULL && !PyTraceBack_Check(tb)) { - PyErr_SetString(PyExc_TypeError, - "throw() third argument must be a traceback object"); - return NULL; - } - - Py_INCREF(typ); - Py_XINCREF(val); - Py_XINCREF(tb); - - if (PyExceptionClass_Check(typ)) { - PyErr_NormalizeException(&typ, &val, &tb); - } - - else if (PyExceptionInstance_Check(typ)) { - /* Raising an instance. The value should be a dummy. */ - if (val && val != Py_None) { - PyErr_SetString(PyExc_TypeError, - "instance exception may not have a separate value"); - goto failed_throw; - } - else { - /* Normalize to raise , */ - Py_XDECREF(val); - val = typ; - typ = PyExceptionInstance_Class(typ); - Py_INCREF(typ); - } - } - else { - /* Not something you can raise. throw() fails. */ - PyErr_Format(PyExc_TypeError, - "exceptions must be classes, or instances, not %s", - typ->ob_type->tp_name); - goto failed_throw; - } - - return throw_greenlet(self, typ, val, tb); - -failed_throw: - /* Didn't use our arguments, so restore their original refcounts */ - Py_DECREF(typ); - Py_XDECREF(val); - Py_XDECREF(tb); - return NULL; -} - -static int green_nonzero(PyGreenlet* self) -{ - return PyGreen_ACTIVE(self); -} - -static PyObject* green_getdead(PyGreenlet* self, void* c) -{ - PyObject* res; - if (PyGreen_ACTIVE(self) || !PyGreen_STARTED(self)) - res = Py_False; - else - res = Py_True; - Py_INCREF(res); - return res; -} - -static PyObject* green_getrun(PyGreenlet* self, void* c) -{ - if (PyGreen_STARTED(self) || self->run_info == NULL) { - PyErr_SetString(PyExc_AttributeError, "run"); - return NULL; - } - Py_INCREF(self->run_info); - return self->run_info; -} - -static int green_setrun(PyGreenlet* self, PyObject* nrun, void* c) -{ - PyObject* o; - if (PyGreen_STARTED(self)) { - PyErr_SetString(PyExc_AttributeError, - "run cannot be set " - "after the start of the greenlet"); - return -1; - } - o = self->run_info; - self->run_info = nrun; - Py_XINCREF(nrun); - Py_XDECREF(o); - return 0; -} - -static PyObject* green_getparent(PyGreenlet* self, void* c) -{ - PyObject* result = self->parent ? (PyObject*) self->parent : Py_None; - Py_INCREF(result); - return result; -} - -static int green_setparent(PyGreenlet* self, PyObject* nparent, void* c) -{ - PyGreenlet* p; - if (nparent == NULL) { - PyErr_SetString(PyExc_AttributeError, "can't delete attribute"); - return -1; - } - if (!PyGreen_Check(nparent)) { - PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); - return -1; - } - for (p=(PyGreenlet*) nparent; p; p=p->parent) { - if (p == self) { - PyErr_SetString(PyExc_ValueError, "cyclic parent chain"); - return -1; - } - } - p = self->parent; - self->parent = (PyGreenlet*) nparent; - Py_INCREF(nparent); - Py_DECREF(p); - return 0; -} - -static PyObject* green_getframe(PyGreenlet* self, void* c) -{ - PyObject* result = self->top_frame ? (PyObject*) self->top_frame : Py_None; - Py_INCREF(result); - return result; -} - - -/***********************************************************/ -/* C interface */ - -PyObject* PyGreen_New(PyObject* run, PyObject* parent) -{ - PyGreenlet* o; - if (!PyGreen_Check(parent)) { - PyErr_SetString(PyExc_TypeError, "parent must be a greenlet"); - return NULL; - } - o = (PyGreenlet*) PyType_GenericAlloc(&PyGreen_Type, 0); - if (o == NULL) - return NULL; - Py_INCREF(run); - o->run_info = run; - Py_INCREF(parent); - o->parent = (PyGreenlet*) parent; - return (PyObject*) o; -} - -PyObject* PyGreen_Current(void) -{ - if (!STATE_OK) - return NULL; - return (PyObject*) ts_current; -} - -PyObject* PyGreen_Switch(PyObject* g, PyObject* value) -{ - PyGreenlet *self; - if (!PyGreen_Check(g)) { - PyErr_BadInternalCall(); - return NULL; - } - self = (PyGreenlet*) g; - Py_XINCREF(value); - if (PyGreen_STARTED(self) && !PyGreen_ACTIVE(self)) - value = g_handle_exit(value); - return single_result(g_switch(self, value)); -} - -int PyGreen_SetParent(PyObject* g, PyObject* nparent) -{ - if (!PyGreen_Check(g)) { - PyErr_BadInternalCall(); - return -1; - } - return green_setparent((PyGreenlet*) g, nparent, NULL); -} - -/***********************************************************/ - - -static PyMethodDef green_methods[] = { - {"switch", (PyCFunction)green_switch, METH_VARARGS, switch_doc}, - {"throw", (PyCFunction)green_throw, METH_VARARGS, throw_doc}, - {NULL, NULL} /* sentinel */ -}; - -static PyGetSetDef green_getsets[] = { - {"run", (getter)green_getrun, - (setter)green_setrun, /*XXX*/ NULL}, - {"parent", (getter)green_getparent, - (setter)green_setparent, /*XXX*/ NULL}, - {"gr_frame", (getter)green_getframe, - NULL, /*XXX*/ NULL}, - {"dead", (getter)green_getdead, - NULL, /*XXX*/ NULL}, - {NULL} -}; - -static PyNumberMethods green_as_number = { - NULL, /* nb_add */ - NULL, /* nb_subtract */ - NULL, /* nb_multiply */ - NULL, /* nb_divide */ - NULL, /* nb_remainder */ - NULL, /* nb_divmod */ - NULL, /* nb_power */ - NULL, /* nb_negative */ - NULL, /* nb_positive */ - NULL, /* nb_absolute */ - (inquiry)green_nonzero, /* nb_nonzero */ -}; - -PyTypeObject PyGreen_Type = { - PyObject_HEAD_INIT(NULL) - 0, - "greenlet.greenlet", - sizeof(PyGreenlet), - 0, - (destructor)green_dealloc, /* tp_dealloc */ - 0, /* tp_print */ - 0, /* tp_getattr */ - 0, /* tp_setattr */ - 0, /* tp_compare */ - 0, /* tp_repr */ - &green_as_number, /* tp_as _number*/ - 0, /* tp_as _sequence*/ - 0, /* tp_as _mapping*/ - 0, /* tp_hash */ - 0, /* tp_call */ - 0, /* tp_str */ - 0, /* tp_getattro */ - 0, /* tp_setattro */ - 0, /* tp_as_buffer*/ - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "greenlet(run=None, parent=None)\n\ -Create a new greenlet object (without running it). \"run\" is the\n\ -callable to invoke, and \"parent\" is the parent greenlet, which\n\ -defaults to the current greenlet.", /* tp_doc */ - 0, /* tp_traverse */ - 0, /* tp_clear */ - 0, /* tp_richcompare */ - offsetof(PyGreenlet, weakreflist), /* tp_weaklistoffset */ - 0, /* tp_iter */ - 0, /* tp_iternext */ - green_methods, /* tp_methods */ - 0, /* tp_members */ - green_getsets, /* tp_getset */ - 0, /* tp_base */ - 0, /* tp_dict */ - 0, /* tp_descr_get */ - 0, /* tp_descr_set */ - 0, /* tp_dictoffset */ - (initproc)green_init, /* tp_init */ - 0, /* tp_alloc */ - green_new, /* tp_new */ -}; -/* XXX need GC support */ - - -static PyObject* mod_getcurrent(PyObject* self) -{ - if (!STATE_OK) - return NULL; - Py_INCREF(ts_current); - return (PyObject*) ts_current; -} - -static PyMethodDef GreenMethods[] = { - {"getcurrent", (PyCFunction)mod_getcurrent, METH_NOARGS, - "greenlet.getcurrent()\n\ -Returns the current greenlet (i.e. the one which called this\n\ -function)."}, - {NULL, NULL} /* Sentinel */ -}; - -static char* copy_on_greentype[] = { - "getcurrent", "error", "GreenletExit", NULL -}; - -void initgreenlet(void) -{ - PyObject* m; - PyObject* greenletexit_doc; - PyObject* greenletexit_dict; - PyObject* greenleterror_doc; - PyObject* greenleterror_dict; - int error; - char** p; - _PyGreen_switchstack = g_switchstack; - _PyGreen_slp_switch = slp_switch; - _PyGreen_initialstub = g_initialstub; - m = Py_InitModule("greenlet", GreenMethods); - - ts_curkey = PyString_InternFromString("__greenlet_ts_curkey"); - ts_delkey = PyString_InternFromString("__greenlet_ts_delkey"); - if (ts_curkey == NULL || ts_delkey == NULL) - return; - if (PyType_Ready(&PyGreen_Type) < 0) - return; - - greenleterror_dict = PyDict_New(); - if (greenleterror_dict == NULL) - return; - greenleterror_doc = PyString_FromString("internal greenlet error"); - if (greenleterror_doc == NULL) { - Py_DECREF(greenleterror_dict); - return; - } - - error = PyDict_SetItemString(greenleterror_dict, "__doc__", greenleterror_doc); - Py_DECREF(greenleterror_doc); - if (error == -1) { - Py_DECREF(greenleterror_dict); - return; - } - - PyExc_GreenletError = PyErr_NewException("py.magic.greenlet.error", NULL, greenleterror_dict); - Py_DECREF(greenleterror_dict); - if (PyExc_GreenletError == NULL) - return; - - greenletexit_dict = PyDict_New(); - if (greenletexit_dict == NULL) - return; - greenletexit_doc = PyString_FromString("greenlet.GreenletExit\n\ -This special exception does not propagate to the parent greenlet; it\n\ -can be used to kill a single greenlet.\n"); - if (greenletexit_doc == NULL) { - Py_DECREF(greenletexit_dict); - return; - } - - error = PyDict_SetItemString(greenletexit_dict, "__doc__", greenletexit_doc); - Py_DECREF(greenletexit_doc); - if (error == -1) { - Py_DECREF(greenletexit_dict); - return; - } - - PyExc_GreenletExit = PyErr_NewException("py.magic.greenlet.GreenletExit", - NULL, greenletexit_dict); - Py_DECREF(greenletexit_dict); - if (PyExc_GreenletExit == NULL) - return; - - ts_current = green_create_main(); - if (ts_current == NULL) - return; - - Py_INCREF(&PyGreen_Type); - PyModule_AddObject(m, "greenlet", (PyObject*) &PyGreen_Type); - Py_INCREF(PyExc_GreenletError); - PyModule_AddObject(m, "error", PyExc_GreenletError); - Py_INCREF(PyExc_GreenletExit); - PyModule_AddObject(m, "GreenletExit", PyExc_GreenletExit); - - /* also publish module-level data as attributes of the greentype. */ - for (p=copy_on_greentype; *p; p++) { - PyObject* o = PyObject_GetAttrString(m, *p); - if (!o) continue; - PyDict_SetItemString(PyGreen_Type.tp_dict, *p, o); - Py_DECREF(o); - } -} diff --git a/py/c-extension/greenlet/greenlet.h b/py/c-extension/greenlet/greenlet.h deleted file mode 100644 index 8996c3b51..000000000 --- a/py/c-extension/greenlet/greenlet.h +++ /dev/null @@ -1,41 +0,0 @@ - -/* Greenlet object interface */ - -#ifndef Py_GREENLETOBJECT_H -#define Py_GREENLETOBJECT_H -#ifdef __cplusplus -extern "C" { -#endif - -#include - -typedef struct _greenlet { - PyObject_HEAD - char* stack_start; - char* stack_stop; - char* stack_copy; - long stack_saved; - struct _greenlet* stack_prev; - struct _greenlet* parent; - PyObject* run_info; - struct _frame* top_frame; - int recursion_depth; - PyObject* weakreflist; -} PyGreenlet; - -extern PyTypeObject PyGreen_Type; - -#define PyGreen_Check(op) PyObject_TypeCheck(op, &PyGreen_Type) -#define PyGreen_STARTED(op) (((PyGreenlet*)(op))->stack_stop != NULL) -#define PyGreen_ACTIVE(op) (((PyGreenlet*)(op))->stack_start != NULL) -#define PyGreen_GET_PARENT(op) (((PyGreenlet*)(op))->parent) - -PyObject* PyGreen_New(PyObject* run, PyObject* parent); -PyObject* PyGreen_Current(void); -PyObject* PyGreen_Switch(PyObject* g, PyObject* args); /* g.switch(*args) */ -int PyGreen_SetParent(PyObject* g, PyObject* nparent); /* g.parent = ... */ - -#ifdef __cplusplus -} -#endif -#endif /* !Py_GREENLETOBJECT_H */ diff --git a/py/c-extension/greenlet/setup.py b/py/c-extension/greenlet/setup.py deleted file mode 100644 index 037c88e33..000000000 --- a/py/c-extension/greenlet/setup.py +++ /dev/null @@ -1,8 +0,0 @@ -from distutils.core import setup -from distutils.extension import Extension - -setup ( name = "greenlet", - version = "0.1", - ext_modules=[Extension(name = 'greenlet', - sources = ['greenlet.c'])] - ) diff --git a/py/c-extension/greenlet/slp_platformselect.h b/py/c-extension/greenlet/slp_platformselect.h deleted file mode 100644 index c19bb0632..000000000 --- a/py/c-extension/greenlet/slp_platformselect.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Platform Selection for Stackless Python - */ - -#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86) -#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */ -#elif defined(__GNUC__) && defined(__amd64__) -#include "switch_amd64_unix.h" /* gcc on amd64 */ -#elif defined(__GNUC__) && defined(__i386__) -#include "switch_x86_unix.h" /* gcc on X86 */ -#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__) -#include "switch_ppc_unix.h" /* gcc on PowerPC */ -#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__) -#include "switch_ppc_macosx.h" /* Apple MacOS X on PowerPC */ -#elif defined(__GNUC__) && defined(sparc) && defined(sun) -#include "switch_sparc_sun_gcc.h" /* SunOS sparc with gcc */ -#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__) -#include "switch_s390_unix.h" /* Linux/S390 */ -#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__) -#include "switch_s390_unix.h" /* Linux/S390 zSeries (identical) */ -#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__) -#include "switch_mips_unix.h" /* Linux/MIPS */ -#endif diff --git a/py/c-extension/greenlet/switch_amd64_unix.h b/py/c-extension/greenlet/switch_amd64_unix.h deleted file mode 100644 index 8d30e7439..000000000 --- a/py/c-extension/greenlet/switch_amd64_unix.h +++ /dev/null @@ -1,66 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 01-Apr-04 Hye-Shik Chang - * Ported from i386 to amd64. - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "rdx", "rbx", "r12", "r13", "r14", "r15" - - -static int -slp_switch(void) -{ - register long *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("movq %%rsp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addq %0, %%rsp\n" - "addq %0, %%rbp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_mips_unix.h b/py/c-extension/greenlet/switch_mips_unix.h deleted file mode 100644 index 94b548917..000000000 --- a/py/c-extension/greenlet/switch_mips_unix.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 05-Jan-08 Thiemo Seufer - * Ported from ppc. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#ifdef __mips64 -#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ - "$23", "$28", "$30" -#else -#define REGS_TO_SAVE "$16", "$17", "$18", "$19", "$20", "$21", "$22", \ - "$23", "$30" -#endif -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ __volatile__ ("" : : : REGS_TO_SAVE); - __asm__ ("move %0, $29" : "=r" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ __volatile__ ( -#ifdef __mips64 - "daddu $29, %0\n" -#else - "addu $29, %0\n" -#endif - : /* no outputs */ - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ __volatile__ ("" : : : REGS_TO_SAVE); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_ppc_macosx.h b/py/c-extension/greenlet/switch_ppc_macosx.h deleted file mode 100644 index 150ec8be0..000000000 --- a/py/c-extension/greenlet/switch_ppc_macosx.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "; asm block 3\n" - "\tmr r11, %0\n" - "\tadd r1, r1, r11\n" - "\tadd r30, r30, r11\n" - : /* no outputs */ - : "g" (stsizediff) - : "r11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_ppc_unix.h b/py/c-extension/greenlet/switch_ppc_unix.h deleted file mode 100644 index 75faf922e..000000000 --- a/py/c-extension/greenlet/switch_ppc_unix.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'r31' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 14-Jan-04 Bob Ippolito - * added cr2-cr4 to the registers to be saved. - * Open questions: Should we save FP registers? - * What about vector registers? - * Differences between darwin and unix? - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 04-Oct-02 Gustavo Niemeyer - * Ported from MacOS version. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 29-Jun-02 Christian Tismer - * Added register 13-29, 31 saves. The same way as - * Armin Rigo did for the x86_unix version. - * This seems to be now fully functional! - * 04-Mar-02 Hye-Shik Chang - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 3 - -/* !!!!WARNING!!!! need to add "r31" in the next line if this header file - * is meant to be compiled non-dynamically! - */ -#define REGS_TO_SAVE "r13", "r14", "r15", "r16", "r17", "r18", "r19", "r20", \ - "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", \ - "cr2", "cr3", "cr4" -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("mr %0, 1" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "mr 11, %0\n" - "add 1, 1, 11\n" - "add 30, 30, 11\n" - : /* no outputs */ - : "g" (stsizediff) - : "11" - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_s390_unix.h b/py/c-extension/greenlet/switch_s390_unix.h deleted file mode 100644 index a96c741e7..000000000 --- a/py/c-extension/greenlet/switch_s390_unix.h +++ /dev/null @@ -1,54 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 06-Oct-02 Gustavo Niemeyer - * Ported to Linux/S390. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -#define REGS_TO_SAVE "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r14", \ - "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", \ - "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15" - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm__ volatile ("" : : : REGS_TO_SAVE); - __asm__ ("lr %0, 15" : "=g" (stackref) : ); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "ar 15, %0" - : /* no outputs */ - : "g" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : REGS_TO_SAVE); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_sparc_sun_gcc.h b/py/c-extension/greenlet/switch_sparc_sun_gcc.h deleted file mode 100644 index f34a0880b..000000000 --- a/py/c-extension/greenlet/switch_sparc_sun_gcc.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * added support for SunOS sparc with gcc - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#include - -#define STACK_MAGIC 0 - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - - /* Put the stack pointer into stackref */ - - /* Sparc special: at first, flush register windows - */ - __asm__ volatile ( - "ta %1\n\t" - "mov %%sp, %0" - : "=r" (stackref) : "i" (ST_FLUSH_WINDOWS)); - - { /* You shalt put SLP_SAVE_STATE into a local block */ - - SLP_SAVE_STATE(stackref, stsizediff); - - /* Increment stack and frame pointer by stsizediff */ - - /* Sparc special: at first load new return address. - This cannot be done later, because the stack - might be overwritten again just after SLP_RESTORE_STATE - has finished. BTW: All other registers (l0-l7 and i0-i5) - might be clobbered too. - */ - __asm__ volatile ( - "ld [%0+60], %%i7\n\t" - "add %1, %%sp, %%sp\n\t" - "add %1, %%fp, %%fp" - : : "r" (_cst->stack), "r" (stsizediff) - : "%l0", "%l1", "%l2", "%l3", "%l4", "%l5", "%l6", "%l7", - "%i0", "%i1", "%i2", "%i3", "%i4", "%i5"); - - SLP_RESTORE_STATE(); - - /* Run far away as fast as possible, don't look back at the sins. - * The LORD rained down burning sulfur on Sodom and Gomorra ... - */ - - /* Sparc special: Must make it *very* clear to the CPU that - it shouldn't look back into the register windows - */ - __asm__ volatile ( "ta %0" : : "i" (ST_CLEAN_WINDOWS)); - return 0; - } -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/switch_x86_msvc.h b/py/c-extension/greenlet/switch_x86_msvc.h deleted file mode 100644 index 0e4c6a377..000000000 --- a/py/c-extension/greenlet/switch_x86_msvc.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 26-Sep-02 Christian Tismer - * again as a result of virtualized stack access, - * the compiler used less registers. Needed to - * explicit mention registers in order to get them saved. - * Thanks to Jeff Senn for pointing this out and help. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for sparc - * 01-Mar-02 Christian Tismer - * Initial final version after lots of iterations for i386. - */ - -#define alloca _alloca - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -#define STACK_MAGIC 0 - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - __asm mov stackref, esp; - /* modify EBX, ESI and EDI in order to get them preserved */ - __asm mov ebx, ebx; - __asm xchg esi, edi; - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm { - mov eax, stsizediff - add esp, eax - add ebp, eax - } - SLP_RESTORE_STATE(); - return 0; - } -} - -#endif - -/* - * further self-processing support - */ - -/* we have IsBadReadPtr available, so we can peek at objects */ -#define STACKLESS_SPY - -#ifdef IMPLEMENT_STACKLESSMODULE -#include "Windows.h" -#define CANNOT_READ_MEM(p, bytes) IsBadReadPtr(p, bytes) - -static int IS_ON_STACK(void*p) -{ - int stackref; - int stackbase = ((int)&stackref) & 0xfffff000; - return (int)p >= stackbase && (int)p < stackbase + 0x00100000; -} - -#endif diff --git a/py/c-extension/greenlet/switch_x86_unix.h b/py/c-extension/greenlet/switch_x86_unix.h deleted file mode 100644 index 1b8719e85..000000000 --- a/py/c-extension/greenlet/switch_x86_unix.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * this is the internal transfer function. - * - * HISTORY - * 07-Sep-05 (py-dev mailing list discussion) - * removed 'ebx' from the register-saved. !!!! WARNING !!!! - * It means that this file can no longer be compiled statically! - * It is now only suitable as part of a dynamic library! - * 24-Nov-02 Christian Tismer - * needed to add another magic constant to insure - * that f in slp_eval_frame(PyFrameObject *f) - * STACK_REFPLUS will probably be 1 in most cases. - * gets included into the saved stack area. - * 17-Sep-02 Christian Tismer - * after virtualizing stack save/restore, the - * stack size shrunk a bit. Needed to introduce - * an adjustment STACK_MAGIC per platform. - * 15-Sep-02 Gerd Woetzel - * slightly changed framework for spark - * 31-Avr-02 Armin Rigo - * Added ebx, esi and edi register-saves. - * 01-Mar-02 Samual M. Rushing - * Ported from i386. - */ - -#define STACK_REFPLUS 1 - -#ifdef SLP_EVAL - -/* #define STACK_MAGIC 3 */ -/* the above works fine with gcc 2.96, but 2.95.3 wants this */ -#define STACK_MAGIC 0 - -static int -slp_switch(void) -{ - register int *stackref, stsizediff; - /* !!!!WARNING!!!! need to add "ebx" in the next line, as well as in the - * last line of this function, if this header file is meant to be compiled - * non-dynamically! - */ - __asm__ volatile ("" : : : "esi", "edi"); - __asm__ ("movl %%esp, %0" : "=g" (stackref)); - { - SLP_SAVE_STATE(stackref, stsizediff); - __asm__ volatile ( - "addl %0, %%esp\n" - "addl %0, %%ebp\n" - : - : "r" (stsizediff) - ); - SLP_RESTORE_STATE(); - } - __asm__ volatile ("" : : : "esi", "edi"); - return 0; -} - -#endif - -/* - * further self-processing support - */ - -/* - * if you want to add self-inspection tools, place them - * here. See the x86_msvc for the necessary defines. - * These features are highly experimental und not - * essential yet. - */ diff --git a/py/c-extension/greenlet/test_generator.py b/py/c-extension/greenlet/test_generator.py deleted file mode 100644 index 66629b88f..000000000 --- a/py/c-extension/greenlet/test_generator.py +++ /dev/null @@ -1,58 +0,0 @@ -import py -try: - from py.magic import greenlet -except (ImportError, RuntimeError), e: - py.test.skip(str(e)) - - - -class genlet(greenlet): - - def __init__(self, *args, **kwds): - self.args = args - self.kwds = kwds - - def run(self): - fn, = self.fn - fn(*self.args, **self.kwds) - - def __iter__(self): - return self - - def next(self): - self.parent = greenlet.getcurrent() - result = self.switch() - if self: - return result - else: - raise StopIteration - -def Yield(value): - g = greenlet.getcurrent() - while not isinstance(g, genlet): - if g is None: - raise RuntimeError, 'yield outside a genlet' - g = g.parent - g.parent.switch(value) - -def generator(func): - class generator(genlet): - fn = (func,) - return generator - -# ____________________________________________________________ - -def test_generator(): - seen = [] - - def g(n): - for i in range(n): - seen.append(i) - Yield(i) - g = generator(g) - - for k in range(3): - for j in g(5): - seen.append(j) - - assert seen == 3 * [0, 0, 1, 1, 2, 2, 3, 3, 4, 4] diff --git a/py/c-extension/greenlet/test_generator_nested.py b/py/c-extension/greenlet/test_generator_nested.py deleted file mode 100644 index 46059a489..000000000 --- a/py/c-extension/greenlet/test_generator_nested.py +++ /dev/null @@ -1,161 +0,0 @@ -from __future__ import generators -import py -try: - from py.magic import greenlet -except (ImportError, RuntimeError), e: - py.test.skip(str(e)) - -class genlet(greenlet): - - def __init__(self, *args, **kwds): - self.args = args - self.kwds = kwds - self.child = None - - def run(self): - fn, = self.fn - fn(*self.args, **self.kwds) - - def __iter__(self): - return self - - def set_child(self, child): - self.child = child - - def next(self): - if self.child: - child = self.child - while child.child: - tmp = child - child = child.child - tmp.child = None - - result = child.switch() - else: - self.parent = greenlet.getcurrent() - result = self.switch() - - if self: - return result - else: - raise StopIteration - -def Yield(value, level = 1): - g = greenlet.getcurrent() - - while level != 0: - if not isinstance(g, genlet): - raise RuntimeError, 'yield outside a genlet' - if level > 1: - g.parent.set_child(g) - g = g.parent - level -= 1 - - g.switch(value) - -def Genlet(func): - class Genlet(genlet): - fn = (func,) - return Genlet - -# ____________________________________________________________ - -def g1(n, seen): - for i in range(n): - seen.append(i+1) - yield i - -def g2(n, seen): - for i in range(n): - seen.append(i+1) - Yield(i) - -g2 = Genlet(g2) - -def nested(i): - Yield(i) - -def g3(n, seen): - for i in range(n): - seen.append(i+1) - nested(i) -g3 = Genlet(g3) - -def test_genlet_simple(): - - for g in [g1, g2, g3]: - seen = [] - for k in range(3): - for j in g(5, seen): - seen.append(j) - - assert seen == 3 * [1, 0, 2, 1, 3, 2, 4, 3, 5, 4] - -def test_genlet_bad(): - try: - Yield(10) - except RuntimeError: - pass - -test_genlet_bad() -test_genlet_simple() -test_genlet_bad() - -def a(n): - if n == 0: - return - for ii in ax(n-1): - Yield(ii) - Yield(n) -ax = Genlet(a) - -def test_nested_genlets(): - seen = [] - for ii in ax(5): - seen.append(ii) - -test_nested_genlets() - -def perms(l): - if len(l) > 1: - for e in l: - # No syntactical sugar for generator expressions - [Yield([e] + p) for p in perms([x for x in l if x!=e])] - else: - Yield(l) - -perms = Genlet(perms) - -def test_perms(): - gen_perms = perms(range(4)) - permutations = list(gen_perms) - assert len(permutations) == 4*3*2*1 - assert [0,1,2,3] in permutations - assert [3,2,1,0] in permutations - res = [] - for ii in zip(perms(range(4)), perms(range(3))): - res.append(ii) - # XXX Test to make sure we are working as a generator expression -test_perms() - - -def gr1(n): - for ii in range(1, n): - Yield(ii) - Yield(ii * ii, 2) - -gr1 = Genlet(gr1) - -def gr2(n, seen): - for ii in gr1(n): - seen.append(ii) - -gr2 = Genlet(gr2) - -def test_layered_genlets(): - seen = [] - for ii in gr2(5, seen): - seen.append(ii) - assert seen == [1, 1, 2, 4, 3, 9, 4, 16] - -test_layered_genlets() diff --git a/py/c-extension/greenlet/test_greenlet.py b/py/c-extension/greenlet/test_greenlet.py deleted file mode 100644 index 4b7d99d46..000000000 --- a/py/c-extension/greenlet/test_greenlet.py +++ /dev/null @@ -1,158 +0,0 @@ -import py -try: - from py.magic import greenlet -except (ImportError, RuntimeError), e: - py.test.skip(str(e)) - -import sys, gc -from py.test import raises -try: - import thread, threading -except ImportError: - thread = None - -def test_simple(): - lst = [] - def f(): - lst.append(1) - greenlet.getcurrent().parent.switch() - lst.append(3) - g = greenlet(f) - lst.append(0) - g.switch() - lst.append(2) - g.switch() - lst.append(4) - assert lst == range(5) - -def test_threads(): - if not thread: - py.test.skip("this is a test about thread") - success = [] - def f(): - test_simple() - success.append(True) - ths = [threading.Thread(target=f) for i in range(10)] - for th in ths: - th.start() - for th in ths: - th.join() - assert len(success) == len(ths) - - -class SomeError(Exception): - pass - -def fmain(seen): - try: - greenlet.getcurrent().parent.switch() - except: - seen.append(sys.exc_info()[0]) - raise - raise SomeError - -def test_exception(): - seen = [] - g1 = greenlet(fmain) - g2 = greenlet(fmain) - g1.switch(seen) - g2.switch(seen) - g2.parent = g1 - assert seen == [] - raises(SomeError, g2.switch) - assert seen == [SomeError] - g2.switch() - assert seen == [SomeError] - -def send_exception(g, exc): - # note: send_exception(g, exc) can be now done with g.throw(exc). - # the purpose of this test is to explicitely check the propagation rules. - def crasher(exc): - raise exc - g1 = greenlet(crasher, parent=g) - g1.switch(exc) - -def test_send_exception(): - seen = [] - g1 = greenlet(fmain) - g1.switch(seen) - raises(KeyError, "send_exception(g1, KeyError)") - assert seen == [KeyError] - -def test_dealloc(): - seen = [] - g1 = greenlet(fmain) - g2 = greenlet(fmain) - g1.switch(seen) - g2.switch(seen) - assert seen == [] - del g1 - gc.collect() - assert seen == [greenlet.GreenletExit] - del g2 - gc.collect() - assert seen == [greenlet.GreenletExit, greenlet.GreenletExit] - -def test_dealloc_other_thread(): - if not thread: - py.test.skip("this is a test about thread") - seen = [] - someref = [] - lock = thread.allocate_lock() - lock.acquire() - lock2 = thread.allocate_lock() - lock2.acquire() - def f(): - g1 = greenlet(fmain) - g1.switch(seen) - someref.append(g1) - del g1 - gc.collect() - lock.release() - lock2.acquire() - greenlet() # trigger release - lock.release() - lock2.acquire() - t = threading.Thread(target=f) - t.start() - lock.acquire() - assert seen == [] - assert len(someref) == 1 - del someref[:] - gc.collect() - # g1 is not released immediately because it's from another thread - assert seen == [] - lock2.release() - lock.acquire() - assert seen == [greenlet.GreenletExit] - lock2.release() - t.join() - -def test_frame(): - def f1(): - f = sys._getframe(0) - assert f.f_back is None - greenlet.getcurrent().parent.switch(f) - return "meaning of life" - g = greenlet(f1) - frame = g.switch() - assert frame is g.gr_frame - assert g - next = g.switch() - assert not g - assert next == "meaning of life" - assert g.gr_frame is None - -def test_thread_bug(): - if not thread: - py.test.skip("this is a test about thread") - import time - def runner(x): - g = greenlet(lambda: time.sleep(x)) - g.switch() - t1 = threading.Thread(target=runner, args=(0.2,)) - t2 = threading.Thread(target=runner, args=(0.3,)) - t1.start() - t2.start() - t1.join() - t2.join() diff --git a/py/c-extension/greenlet/test_remote.py b/py/c-extension/greenlet/test_remote.py deleted file mode 100644 index 97ac0d88b..000000000 --- a/py/c-extension/greenlet/test_remote.py +++ /dev/null @@ -1,66 +0,0 @@ -import py -try: - from py.magic import greenlet -except (ImportError, RuntimeError), e: - py.test.skip(str(e)) - - -class RGreenletBunch: - - def __init__(self, gateway): - self.channel = gateway.remote_exec(''' - from py.magic import greenlet - glob = {"greenlet": greenlet} - gids = {} - while True: - key, code, args = channel.receive() - if args is not None: - if code is not None: - def run(code=code): - exec code in glob, {} - gids[key] = greenlet(run) - result = gids[key].switch(*args) - channel.send(result) - else: - del gids[key] - ''') - - def greenlet(self, code): - return RGreenlet(self, code) - - -class RGreenlet: - - def __init__(self, bunch, code): - self.channel = bunch.channel - self.code = str(py.code.Source(code)) - - def switch(self, *args): - self.channel.send((id(self), self.code, args)) - self.code = None # only send over the code the first time - return self.channel.receive() - - def __del__(self): - if self.code is None: - self.channel.send((id(self), None, None)) - - -def test_rgreenlet(): - gw = py.execnet.PopenGateway() - bunch = RGreenletBunch(gw) - g = bunch.greenlet(''' - x = greenlet.getcurrent().parent.switch(42) - y = greenlet.getcurrent().parent.switch(x+1) - greenlet.getcurrent().parent.switch(y+2) - import os - greenlet.getcurrent().parent.switch(os.getpid()) - ''') - result = g.switch() - assert result == 42 - result = g.switch(102) - assert result == 103 - result = g.switch(-93) - assert result == -91 - import os - result = g.switch() - assert result != os.getpid() diff --git a/py/c-extension/greenlet/test_throw.py b/py/c-extension/greenlet/test_throw.py deleted file mode 100644 index 16ff08bbb..000000000 --- a/py/c-extension/greenlet/test_throw.py +++ /dev/null @@ -1,97 +0,0 @@ -import py -try: - from py.magic import greenlet -except (ImportError, RuntimeError), e: - py.test.skip(str(e)) - -def switch(*args): - return greenlet.getcurrent().parent.switch(*args) - -def test_class(): - def f(): - try: - switch("ok") - except RuntimeError: - switch("ok") - return - switch("fail") - - g = greenlet(f) - res = g.switch() - assert res == "ok" - res = g.throw(RuntimeError) - assert res == "ok" - -def test_val(): - def f(): - try: - switch("ok") - except RuntimeError, val: - if str(val) == "ciao": - switch("ok") - return - switch("fail") - - g = greenlet(f) - res = g.switch() - assert res == "ok" - res = g.throw(RuntimeError("ciao")) - assert res == "ok" - - g = greenlet(f) - res = g.switch() - assert res == "ok" - res = g.throw(RuntimeError, "ciao") - assert res == "ok" - -def test_kill(): - def f(): - switch("ok") - switch("fail") - - g = greenlet(f) - res = g.switch() - assert res == "ok" - res = g.throw() - assert isinstance(res, greenlet.GreenletExit) - assert g.dead - res = g.throw() # immediately eaten by the already-dead greenlet - assert isinstance(res, greenlet.GreenletExit) - -def test_throw_goes_to_original_parent(): - main = greenlet.getcurrent() - def f1(): - try: - main.switch("f1 ready to catch") - except IndexError: - return "caught" - else: - return "normal exit" - def f2(): - main.switch("from f2") - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - py.test.raises(IndexError, g2.throw, IndexError) - assert g2.dead - assert g1.dead - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - res = g1.switch() - assert res == "f1 ready to catch" - res = g2.throw(IndexError) - assert res == "caught" - assert g2.dead - assert g1.dead - - g1 = greenlet(f1) - g2 = greenlet(f2, parent=g1) - res = g1.switch() - assert res == "f1 ready to catch" - res = g2.switch() - assert res == "from f2" - res = g2.throw(IndexError) - assert res == "caught" - assert g2.dead - assert g1.dead diff --git a/py/doc/greenlet.txt b/py/doc/greenlet.txt deleted file mode 100644 index 47ba76bdf..000000000 --- a/py/doc/greenlet.txt +++ /dev/null @@ -1,315 +0,0 @@ -===================================================== -py.magic.greenlet: Lightweight concurrent programming -===================================================== - -.. contents:: -.. sectnum:: - -Motivation -========== - -The "greenlet" package is a spin-off of `Stackless`_, a version of CPython -that supports micro-threads called "tasklets". Tasklets run -pseudo-concurrently (typically in a single or a few OS-level threads) and -are synchronized with data exchanges on "channels". - -A "greenlet", on the other hand, is a still more primitive notion of -micro-thread with no implicit scheduling; coroutines, in other words. -This is useful when you want to -control exactly when your code runs. You can build custom scheduled -micro-threads on top of greenlet; however, it seems that greenlets are -useful on their own as a way to make advanced control flow structures. -For example, we can recreate generators; the difference with Python's own -generators is that our generators can call nested functions and the nested -functions can yield values too. (Additionally, you don't need a "yield" -keyword. See the example in :source:`py/c-extension/greenlet/test_generator.py`). - -Greenlets are provided as a C extension module for the regular unmodified -interpreter. - -.. _`Stackless`: http://www.stackless.com - -Example -------- - -Let's consider a system controlled by a terminal-like console, where the user -types commands. Assume that the input comes character by character. In such -a system, there will typically be a loop like the following one:: - - def process_commands(*args): - while True: - line = '' - while not line.endswith('\n'): - line += read_next_char() - if line == 'quit\n': - print "are you sure?" - if read_next_char() != 'y': - continue # ignore the command - process_command(line) - -Now assume that you want to plug this program into a GUI. Most GUI toolkits -are event-based. They will invoke a call-back for each character the user -presses. [Replace "GUI" with "XML expat parser" if that rings more bells to -you ``:-)``] In this setting, it is difficult to implement the -read_next_char() function needed by the code above. We have two incompatible -functions:: - - def event_keydown(key): - ?? - - def read_next_char(): - ?? should wait for the next event_keydown() call - -You might consider doing that with threads. Greenlets are an alternate -solution that don't have the related locking and shutdown problems. You -start the process_commands() function in its own, separate greenlet, and -then you exchange the keypresses with it as follows:: - - def event_keydown(key): - # jump into g_processor, sending it the key - g_processor.switch(key) - - def read_next_char(): - # g_self is g_processor in this simple example - g_self = greenlet.getcurrent() - # jump to the parent (main) greenlet, waiting for the next key - next_char = g_self.parent.switch() - return next_char - - g_processor = greenlet(process_commands) - g_processor.switch(*args) # input arguments to process_commands() - - gui.mainloop() - -In this example, the execution flow is: when read_next_char() is called, it -is part of the g_processor greenlet, so when it switches to its parent -greenlet, it resumes execution in the top-level main loop (the GUI). When -the GUI calls event_keydown(), it switches to g_processor, which means that -the execution jumps back wherever it was suspended in that greenlet -- in -this case, to the switch() instruction in read_next_char() -- and the ``key`` -argument in event_keydown() is passed as the return value of the switch() in -read_next_char(). - -Note that read_next_char() will be suspended and resumed with its call stack -preserved, so that it will itself return to different positions in -process_commands() depending on where it was originally called from. This -allows the logic of the program to be kept in a nice control-flow way; we -don't have to completely rewrite process_commands() to turn it into a state -machine. - - -Usage -===== - -Introduction ------------- - -A "greenlet" is a small independent pseudo-thread. Think about it as a -small stack of frames; the outermost (bottom) frame is the initial -function you called, and the innermost frame is the one in which the -greenlet is currently paused. You work with greenlets by creating a -number of such stacks and jumping execution between them. Jumps are never -implicit: a greenlet must choose to jump to another greenlet, which will -cause the former to suspend and the latter to resume where it was -suspended. Jumping between greenlets is called "switching". - -When you create a greenlet, it gets an initially empty stack; when you -first switch to it, it starts the run a specified function, which may call -other functions, switch out of the greenlet, etc. When eventually the -outermost function finishes its execution, the greenlet's stack becomes -empty again and the greenlet is "dead". Greenlets can also die of an -uncaught exception. - -For example:: - - from py.magic import greenlet - - def test1(): - print 12 - gr2.switch() - print 34 - - def test2(): - print 56 - gr1.switch() - print 78 - - gr1 = greenlet(test1) - gr2 = greenlet(test2) - gr1.switch() - -The last line jumps to test1, which prints 12, jumps to test2, prints 56, -jumps back into test1, prints 34; and then test1 finishes and gr1 dies. -At this point, the execution comes back to the original ``gr1.switch()`` -call. Note that 78 is never printed. - -Parents -------- - -Let's see where execution goes when a greenlet dies. Every greenlet has a -"parent" greenlet. The parent greenlet is initially the one in which the -greenlet was created (this can be changed at any time). The parent is -where execution continues when a greenlet dies. This way, greenlets are -organized in a tree. Top-level code that doesn't run in a user-created -greenlet runs in the implicit "main" greenlet, which is the root of the -tree. - -In the above example, both gr1 and gr2 have the main greenlet as a parent. -Whenever one of them dies, the execution comes back to "main". - -Uncaught exceptions are propagated into the parent, too. For example, if -the above test2() contained a typo, it would generate a NameError that -would kill gr2, and the exception would go back directly into "main". -The traceback would show test2, but not test1. Remember, switches are not -calls, but transfer of execution between parallel "stack containers", and -the "parent" defines which stack logically comes "below" the current one. - -Instantiation -------------- - -``py.magic.greenlet`` is the greenlet type, which supports the following -operations: - -``greenlet(run=None, parent=None)`` - Create a new greenlet object (without running it). ``run`` is the - callable to invoke, and ``parent`` is the parent greenlet, which - defaults to the current greenlet. - -``greenlet.getcurrent()`` - Returns the current greenlet (i.e. the one which called this - function). - -``greenlet.GreenletExit`` - This special exception does not propagate to the parent greenlet; it - can be used to kill a single greenlet. - -The ``greenlet`` type can be subclassed, too. A greenlet runs by calling -its ``run`` attribute, which is normally set when the greenlet is -created; but for subclasses it also makes sense to define a ``run`` method -instead of giving a ``run`` argument to the constructor. - -Switching ---------- - -Switches between greenlets occur when the method switch() of a greenlet is -called, in which case execution jumps to the greenlet whose switch() is -called, or when a greenlet dies, in which case execution jumps to the -parent greenlet. During a switch, an object or an exception is "sent" to -the target greenlet; this can be used as a convenient way to pass -information between greenlets. For example:: - - def test1(x, y): - z = gr2.switch(x+y) - print z - - def test2(u): - print u - gr1.switch(42) - - gr1 = greenlet(test1) - gr2 = greenlet(test2) - gr1.switch("hello", " world") - -This prints "hello world" and 42, with the same order of execution as the -previous example. Note that the arguments of test1() and test2() are not -provided when the greenlet is created, but only the first time someone -switches to it. - -Here are the precise rules for sending objects around: - -``g.switch(obj=None or *args)`` - Switches execution to the greenlet ``g``, sending it the given - ``obj``. As a special case, if ``g`` did not start yet, then it will - start to run now; in this case, any number of arguments can be - provided, and ``g.run(*args)`` is called. - -Dying greenlet - If a greenlet's ``run()`` finishes, its return value is the object - sent to its parent. If ``run()`` terminates with an exception, the - exception is propagated to its parent (unless it is a - ``greenlet.GreenletExit`` exception, in which case the exception - object is caught and *returned* to the parent). - -Apart from the cases described above, the target greenlet normally -receives the object as the return value of the call to ``switch()`` in -which it was previously suspended. Indeed, although a call to -``switch()`` does not return immediately, it will still return at some -point in the future, when some other greenlet switches back. When this -occurs, then execution resumes just after the ``switch()`` where it was -suspended, and the ``switch()`` itself appears to return the object that -was just sent. This means that ``x = g.switch(y)`` will send the object -``y`` to ``g``, and will later put the (unrelated) object that some -(unrelated) greenlet passes back to us into ``x``. - -Note that any attempt to switch to a dead greenlet actually goes to the -dead greenlet's parent, or its parent's parent, and so on. (The final -parent is the "main" greenlet, which is never dead.) - -Methods and attributes of greenlets ------------------------------------ - -``g.switch(obj=None or *args)`` - Switches execution to the greenlet ``g``. See above. - -``g.run`` - The callable that ``g`` will run when it starts. After ``g`` started, - this attribute no longer exists. - -``g.parent`` - The parent greenlet. This is writeable, but it is not allowed to - create cycles of parents. - -``g.gr_frame`` - The current top frame, or None. - -``g.dead`` - True if ``g`` is dead (i.e. it finished its execution). - -``bool(g)`` - True if ``g`` is active, False if it is dead or not yet started. - -``g.throw([typ, [val, [tb]]])`` - Switches execution to the greenlet ``g``, but immediately raises the - given exception in ``g``. If no argument is provided, the exception - defaults to ``greenlet.GreenletExit``. The normal exception - propagation rules apply, as described above. Note that calling this - method is almost equivalent to the following:: - - def raiser(): - raise typ, val, tb - g_raiser = greenlet(raiser, parent=g) - g_raiser.switch() - - except that this trick does not work for the - ``greenlet.GreenletExit`` exception, which would not propagate - from ``g_raiser`` to ``g``. - -Greenlets and Python threads ----------------------------- - -Greenlets can be combined with Python threads; in this case, each thread -contains an independent "main" greenlet with a tree of sub-greenlets. It -is not possible to mix or switch between greenlets belonging to different -threads. - -Garbage-collecting live greenlets ---------------------------------- - -If all the references to a greenlet object go away (including the -references from the parent attribute of other greenlets), then there is no -way to ever switch back to this greenlet. In this case, a GreenletExit -exception is generated into the greenlet. This is the only case where a -greenlet receives the execution asynchronously. This gives -``try:finally:`` blocks a chance to clean up resources held by the -greenlet. This feature also enables a programming style in which -greenlets are infinite loops waiting for data and processing it. Such -loops are automatically interrupted when the last reference to the -greenlet goes away. - -The greenlet is expected to either die or be resurrected by having a new -reference to it stored somewhere; just catching and ignoring the -GreenletExit is likely to lead to an infinite loop. - -Greenlets do not participate in garbage collection; cycles involving data -that is present in a greenlet's frames will not be detected. Storing -references to other greenlets cyclically may lead to leaks. diff --git a/py/doc/index.txt b/py/doc/index.txt index 74fbfb3d9..9179f9376 100644 --- a/py/doc/index.txt +++ b/py/doc/index.txt @@ -5,13 +5,10 @@ py lib: Main tools and APIs `py.execnet`_ rapidly deploy local or remote processes from your program. -`py.magic.greenlet`_: instantiate thousands of micro-threads from your program. - `py.path`_: use path objects to transparently access local and svn filesystems. `py.code`_: generate python code and use advanced introspection/traceback support. - minor support functionality --------------------------------- @@ -29,7 +26,6 @@ minor support functionality .. _`download and installation`: download.html .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev .. _`py.execnet`: execnet.html -.. _`py.magic.greenlet`: greenlet.html .. _`py.log`: log.html .. _`py.io`: io.html .. _`py.path`: path.html