[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.test`_: cross-project testing tool with many advanced features
|
||||||
- `py.execnet`_: ad-hoc code distribution to SSH, Socket and local sub processes
|
- `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.path`_: path abstractions over local and subversion files
|
||||||
- `py.code`_: dynamic code compile and traceback printing support
|
- `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.test`: http://pylib.org/test.html
|
||||||
.. _`py.execnet`: http://pylib.org/execnet.html
|
.. _`py.execnet`: http://pylib.org/execnet.html
|
||||||
.. _`py.magic.greenlet`: http://pylib.org/greenlet.html
|
|
||||||
.. _`py.path`: http://pylib.org/path.html
|
.. _`py.path`: http://pylib.org/path.html
|
||||||
.. _`py.code`: http://pylib.org/code.html
|
.. _`py.code`: http://pylib.org/code.html
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from initpkg import initpkg
|
from initpkg import initpkg
|
||||||
|
|
||||||
version = "1.0.0a7"
|
version = "1.0.0a8"
|
||||||
|
|
||||||
initpkg(__name__,
|
initpkg(__name__,
|
||||||
description = "pylib and py.test: agile development and test support library",
|
description = "pylib and py.test: agile development and test support library",
|
||||||
revision = int('$LastChangedRevision: 63319 $'.split(':')[1][:-1]),
|
revision = int('$LastChangedRevision: 63340 $'.split(':')[1][:-1]),
|
||||||
lastchangedate = '$LastChangedDate: 2009-03-25 12:50:57 +0100 (Wed, 25 Mar 2009) $',
|
lastchangedate = '$LastChangedDate: 2009-03-26 10:33:50 +0100 (Thu, 26 Mar 2009) $',
|
||||||
version = version,
|
version = version,
|
||||||
url = "http://pylib.org",
|
url = "http://pylib.org",
|
||||||
download_url = "http://codespeak.net/py/%s/download.html" % version,
|
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.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.path`_: use path objects to transparently access local and svn filesystems.
|
||||||
|
|
||||||
`py.code`_: generate python code and use advanced introspection/traceback support.
|
`py.code`_: generate python code and use advanced introspection/traceback support.
|
||||||
|
|
||||||
|
|
||||||
minor support functionality
|
minor support functionality
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
|
@ -29,7 +26,6 @@ minor support functionality
|
||||||
.. _`download and installation`: download.html
|
.. _`download and installation`: download.html
|
||||||
.. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev
|
.. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev
|
||||||
.. _`py.execnet`: execnet.html
|
.. _`py.execnet`: execnet.html
|
||||||
.. _`py.magic.greenlet`: greenlet.html
|
|
||||||
.. _`py.log`: log.html
|
.. _`py.log`: log.html
|
||||||
.. _`py.io`: io.html
|
.. _`py.io`: io.html
|
||||||
.. _`py.path`: path.html
|
.. _`py.path`: path.html
|
||||||
|
|
Loading…
Reference in New Issue