[svn r63340] remove greenlet from py lib
--HG-- branch : trunk
This commit is contained in:
parent
92e354a486
commit
c2ee8273b0
|
@ -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,
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
#
|
|
@ -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
|
|
@ -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_
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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 */
|
|
@ -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'])]
|
||||
)
|
|
@ -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
|
|
@ -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.
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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.
|
||||
*/
|
|
@ -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
|
|
@ -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.
|
||||
*/
|
|
@ -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]
|
|
@ -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()
|
|
@ -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()
|
|
@ -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()
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue