[svn r63340] remove greenlet from py lib

--HG--
branch : trunk
This commit is contained in:
hpk 2009-03-26 10:33:50 +01:00
parent 92e354a486
commit c2ee8273b0
23 changed files with 3 additions and 2642 deletions

View File

@ -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,

View File

@ -1 +0,0 @@
#

View File

@ -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

View File

@ -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_

View File

@ -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 <class>, <instance> */
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);
}
}

View File

@ -1,41 +0,0 @@
/* Greenlet object interface */
#ifndef Py_GREENLETOBJECT_H
#define Py_GREENLETOBJECT_H
#ifdef __cplusplus
extern "C" {
#endif
#include <Python.h>
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 */

View File

@ -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'])]
)

View File

@ -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

View File

@ -1,66 +0,0 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 01-Apr-04 Hye-Shik Chang <perky@FreeBSD.org>
* Ported from i386 to amd64.
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for spark
* 31-Avr-02 Armin Rigo <arigo@ulb.ac.be>
* Added ebx, esi and edi register-saves.
* 01-Mar-02 Samual M. Rushing <rushing@ironport.com>
* 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.
*/

View File

@ -1,56 +0,0 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 05-Jan-08 Thiemo Seufer <ths@debian.org>
* 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.
*/

View File

@ -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 <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* 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.
*/

View File

@ -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 <bob@redivi.com>
* 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 <tismer@tismer.com>
* 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 <niemeyer@conectiva.com>
* Ported from MacOS version.
* 17-Sep-02 Christian Tismer <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 29-Jun-02 Christian Tismer <tismer@tismer.com>
* 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 <perky@fallin.lv>
* 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.
*/

View File

@ -1,54 +0,0 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <niemeyer@conectiva.com>
* 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.
*/

View File

@ -1,85 +0,0 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* added support for SunOS sparc with gcc
*/
#define STACK_REFPLUS 1
#ifdef SLP_EVAL
#include <sys/trap.h>
#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.
*/

View File

@ -1,73 +0,0 @@
/*
* this is the internal transfer function.
*
* HISTORY
* 24-Nov-02 Christian Tismer <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for sparc
* 01-Mar-02 Christian Tismer <tismer@tismer.com>
* 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

View File

@ -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 <tismer@tismer.com>
* 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 <tismer@tismer.com>
* 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 <gerd.woetzel@GMD.DE>
* slightly changed framework for spark
* 31-Avr-02 Armin Rigo <arigo@ulb.ac.be>
* Added ebx, esi and edi register-saves.
* 01-Mar-02 Samual M. Rushing <rushing@ironport.com>
* 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.
*/

View File

@ -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]

View File

@ -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()

View File

@ -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()

View File

@ -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()

View File

@ -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

View File

@ -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.

View File

@ -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