diff --git a/Modules/cPickle.c b/Modules/cPickle.c
index c90b2492d38343..931557e95a2f36 100644
--- a/Modules/cPickle.c
+++ b/Modules/cPickle.c
@@ -2,7 +2,6 @@
#include "cStringIO.h"
#include "structmember.h"
#include "core/stackless_impl.h"
-#include "platf/slp_platformselect.h"
PyDoc_STRVAR(cPickle_module_documentation,
"C implementation and optimization of the Python pickle module.");
@@ -2571,9 +2570,9 @@ save(Picklerobject *self, PyObject *args, int pers_save)
int res = -1;
int tmp;
-#ifdef STACKLESS
+#if defined STACKLESS
/* but we save the stack after a fixed watermark */
- if (CSTACK_SAVE_NOW(PyThreadState_GET(), self)) {
+ if (slp_cstack_save_now(PyThreadState_GET())) {
res = slp_safe_pickling((int(*)(PyObject *, PyObject *, int))&save,
(PyObject *)self, args, pers_save);
goto finally;
diff --git a/PCbuild/pythoncore.vcproj b/PCbuild/pythoncore.vcproj
index 146d89baac291f..0afc880e286c36 100644
--- a/PCbuild/pythoncore.vcproj
+++ b/PCbuild/pythoncore.vcproj
@@ -16,6 +16,9 @@
/>
+
+
@@ -105,6 +111,9 @@
+
@@ -181,6 +190,9 @@
+
@@ -258,6 +270,9 @@
+
@@ -336,6 +351,9 @@
+
@@ -410,6 +428,9 @@
+
@@ -486,6 +507,9 @@
+
@@ -560,6 +584,9 @@
+
@@ -655,11 +682,11 @@
>
-
-
@@ -1954,155 +1977,135 @@
>
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
diff --git a/Python/ceval.c b/Python/ceval.c
index f0addb5d3ee4b3..3f98a4b2f984ad 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -17,7 +17,6 @@
#include "opcode.h"
#include "structmember.h"
#include "core/stackless_impl.h"
-#include "platf/slp_platformselect.h" /* for stack saving */
#include
@@ -906,7 +905,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
return NULL;
#ifdef STACKLESS
- if (CSTACK_SAVE_NOW(tstate, f))
+ if (slp_cstack_save_now(tstate))
return slp_eval_frame_newstack(f, throwflag, retval);
/* push frame */
diff --git a/Stackless/core/slp_transfer.c b/Stackless/core/slp_transfer.c
index 2fc8b189f029b1..efac1a1152b330 100644
--- a/Stackless/core/slp_transfer.c
+++ b/Stackless/core/slp_transfer.c
@@ -80,35 +80,6 @@ extern int slp_switch(void);
#endif
-/* a write only variable used to prevent overly optimisation */
-intptr_t *global_goobledigoobs;
-static int
-climb_stack_and_transfer(PyCStackObject **cstprev, PyCStackObject *cst,
- PyTaskletObject *prev)
-{
- /*
- * there are cases where we have been initialized
- * in some deep stack recursion, but later on we
- * need to switch from a higher stacklevel, and the
- * needed stack size becomes *negative* :-))
- */
- PyThreadState *ts = PyThreadState_GET();
- intptr_t probe;
- register ptrdiff_t needed = &probe - ts->st.cstack_base;
- /* in rare cases, the need might have vanished due to the recursion */
- register intptr_t *goobledigoobs;
- if (needed > 0) {
- goobledigoobs = alloca(needed * sizeof(intptr_t));
- if (goobledigoobs == NULL)
- return -1;
- /* hinder the compiler to optimise away
- goobledigoobs and the alloca call.
- This happens with gcc 4.7.x and -O2 */
- global_goobledigoobs = goobledigoobs;
- }
- return slp_transfer(cstprev, cst, prev);
-}
-
/* This function returns -1 on error, 1 if a switch occurred and 0
* if only a stack save was performed
*/
@@ -122,9 +93,7 @@ slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst,
/* since we change the stack we must assure that the protocol was met */
STACKLESS_ASSERT();
- if ((intptr_t *) &ts > ts->st.cstack_base)
- return climb_stack_and_transfer(cstprev, cst, prev);
- if (cst == NULL || cst->ob_size == 0)
+ if (cst == NULL)
cst = ts->st.initial_stub;
if (cst != NULL) {
if (cst->tstate != ts) {
@@ -145,6 +114,10 @@ slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst,
if (cstprev && *cstprev == cst && cst->ob_refcnt == 1)
cst = NULL;
}
+ /* allocate a fresh
+ if (!slp_cstack_new(cstprev, NULL, t))
+ return NULL;
+
_cstprev = cstprev;
_cst = cst;
_prev = prev;
diff --git a/Stackless/core/stackless_impl.h b/Stackless/core/stackless_impl.h
index 62fbaa6ab4fa7c..2e39ed8f2c5f39 100644
--- a/Stackless/core/stackless_impl.h
+++ b/Stackless/core/stackless_impl.h
@@ -36,23 +36,7 @@ extern "C" {
PyAPI_DATA(int) slp_enable_softswitch;
PyAPI_DATA(int) slp_in_psyco;
PyAPI_DATA(int) slp_try_stackless;
-PyAPI_DATA(PyCStackObject *) slp_cstack_chain;
-
-PyAPI_FUNC(PyCStackObject *) slp_cstack_new(PyCStackObject **cst,
- intptr_t *stackref,
- PyTaskletObject *task);
-PyAPI_FUNC(size_t) slp_cstack_save(PyCStackObject *cstprev);
-PyAPI_FUNC(void) slp_cstack_restore(PyCStackObject *cst);
-
-PyAPI_FUNC(int) slp_transfer(PyCStackObject **cstprev, PyCStackObject *cst,
- PyTaskletObject *prev);
-
-#ifdef Py_DEBUG
-PyAPI_FUNC(int) slp_transfer_return(PyCStackObject *cst);
-#else
-#define slp_transfer_return(cst) \
- slp_transfer(NULL, (cst), NULL)
-#endif
+PyAPI_DATA(PyTaskletObject *) slp_tasklet_chain;
PyAPI_FUNC(int) _PyStackless_InitTypes(void);
PyAPI_FUNC(void) _PyStackless_Init(void);
diff --git a/Stackless/core/stackless_structs.h b/Stackless/core/stackless_structs.h
index e4c31505d680e3..8bf5325a9e48d4 100644
--- a/Stackless/core/stackless_structs.h
+++ b/Stackless/core/stackless_structs.h
@@ -5,10 +5,7 @@
extern "C" {
#endif
-/* platform specific constants (mainly SEH stuff to store )*/
-#include "platf/slp_platformselect.h"
-
-
+#include "stackless_tealet.h"
/*** important structures: tasklet ***/
@@ -62,7 +59,7 @@ extern "C" {
***************************************************************************/
-typedef struct _tasklet_flags {
+typedef struct PyTaskletObject_flags {
int blocked: 2;
unsigned int atomic: 1;
unsigned int ignore_nesting: 1;
@@ -73,46 +70,25 @@ typedef struct _tasklet_flags {
} PyTaskletFlagStruc;
-typedef struct _tasklet {
+typedef struct PyTaskletObject {
PyObject_HEAD
- struct _tasklet *next;
- struct _tasklet *prev;
+ struct PyTaskletObject *next;
+ struct PyTaskletObject *prev;
union {
struct _frame *frame;
struct _cframe *cframe;
} f;
PyObject *tempval;
/* bits stuff */
- struct _tasklet_flags flags;
+ struct PyTaskletObject_flags flags;
int recursion_depth;
- struct _cstack *cstate;
+ int nesting_level;
+ PyThreadState *tstate;
+ struct tealet_t *cstate;
PyObject *def_globals;
PyObject *tsk_weakreflist;
} PyTaskletObject;
-
-/*** important structures: cstack ***/
-
-typedef struct _cstack {
- PyObject_VAR_HEAD
- struct _cstack *next;
- struct _cstack *prev;
-#ifdef have_long_long
- long_long serial;
-#else
- long serial;
-#endif
- struct _tasklet *task;
- int nesting_level;
- PyThreadState *tstate;
-#ifdef _SEH32
- DWORD exception_list; /* SEH handler on Win32 */
-#endif
- intptr_t *startaddr;
- intptr_t stack[1];
-} PyCStackObject;
-
-
/*** important structures: bomb ***/
typedef struct _bomb {
@@ -156,8 +132,8 @@ typedef struct _channel_flags {
typedef struct _channel {
PyObject_HEAD
/* make sure that these fit tasklet's next/prev */
- struct _tasklet *head;
- struct _tasklet *tail;
+ struct PyTaskletObject *head;
+ struct PyTaskletObject *tail;
int balance;
struct _channel_flags flags;
PyObject *chan_weakreflist;
@@ -204,9 +180,6 @@ typedef struct _slpmodule {
PyAPI_DATA(PyTypeObject) PyCFrame_Type;
#define PyCFrame_Check(op) ((op)->ob_type == &PyCFrame_Type)
-PyAPI_DATA(PyTypeObject) PyCStack_Type;
-#define PyCStack_Check(op) ((op)->ob_type == &PyCStack_Type)
-
PyAPI_DATA(PyTypeObject) PyBomb_Type;
#define PyBomb_Check(op) ((op)->ob_type == &PyBomb_Type)
diff --git a/Stackless/core/stackless_tealet.c b/Stackless/core/stackless_tealet.c
new file mode 100644
index 00000000000000..a1e1ddd5e33d29
--- /dev/null
+++ b/Stackless/core/stackless_tealet.c
@@ -0,0 +1,366 @@
+
+#include "stackless_tealet.h"
+
+#include
+#include "frameobject.h"
+#include "stackless_impl.h"
+
+PyTealet_data *slp_tealet_list = NULL;
+
+static void
+slp_tealet_link(tealet_t *t, PyTaskletObject *tasklet)
+{
+ PyTealet_data *td = TEALET_EXTRA(t, PyTealet_data);
+ if (slp_tealet_list) {
+ td->next = slp_tealet_list;
+ td->prev = td->next->prev;
+ td->next->prev = td;
+ td->prev->next = td;
+ } else
+ td->next = td->prev = td;
+ slp_tealet_list = td;
+ td->tasklet = tasklet;
+}
+
+static void
+slp_tealet_unlink(tealet_t *t)
+{
+ PyTealet_data *td = TEALET_EXTRA(t, PyTealet_data);
+ if (td->next != td) {
+ slp_tealet_list = td->next;
+ td->next->prev = td->prev;
+ td->prev->next = td->next;
+ } else
+ slp_tealet_list = NULL;
+}
+
+/****************************************************************
+ *Implement copyable tealet stubs by using a trampoline
+ */
+struct slp_stub_arg
+{
+ tealet_t *current;
+ tealet_run_t run;
+ void *runarg;
+};
+
+static tealet_t *slp_stub_main(tealet_t *current, void *arg)
+{
+ void *myarg = 0;
+ /* the caller is in arg, return right back to him */
+ tealet_switch((tealet_t*)arg, &myarg);
+ /* now we are back, myarg should contain the arg to the run function.
+ * We were possibly duplicated, so can't trust the original function args.
+ */
+ {
+ struct slp_stub_arg sarg = *(struct slp_stub_arg*)myarg;
+ tealet_free(sarg.current, myarg);
+ return (sarg.run)(sarg.current, sarg.runarg);
+ }
+}
+
+/* create a stub and return it */
+tealet_t *slp_stub_new(tealet_t *t, size_t extrasize)
+{
+ void *arg = (void*)tealet_current(t);
+ return tealet_new(t, slp_stub_main, &arg, extrasize);
+}
+
+/* find the stack far point if a stub were created here */
+void * slp_stub_far(tealet_t *dummy1, size_t dummy2)
+{
+ void *arg = 0;
+ return tealet_new_far(dummy1, NULL, &arg, dummy2);
+}
+
+
+/* run a stub */
+int slp_stub_run(tealet_t *stub, tealet_run_t run, void **parg)
+{
+ int result;
+ void *myarg;
+
+ /* we cannot pass arguments to a different tealet on the stack */
+ struct slp_stub_arg *psarg = (struct slp_stub_arg*)tealet_malloc(stub, sizeof(struct slp_stub_arg));
+ if (!psarg)
+ return TEALET_ERR_MEM;
+ psarg->current = stub;
+ psarg->run = run;
+ psarg->runarg = parg ? *parg : NULL;
+ myarg = (void*)psarg;
+ result = tealet_switch(stub, &myarg);
+ if (result) {
+ /* failure */
+ tealet_free(stub, psarg);
+ return result;
+ }
+ /* pass back the arg value from the switch */
+ if (parg)
+ *parg = myarg;
+ return 0;
+}
+
+/* end of generic tealet stub code */
+
+/* translate tealet errors into python errors */
+static int
+slp_tealet_error(int err)
+{
+ assert(err);
+ if (err == TEALET_ERR_MEM)
+ PyErr_NoMemory();
+ assert(err == TEALET_ERR_DEFUNCT);
+ PyErr_SetString(PyExc_RuntimeError, "Tealet corrupt");
+ return -1;
+}
+
+/* the current mechanism is based on the generic callable stubs
+ * above. This is useful, because it allows us to call arbitrary
+ * callbacks, e.g. when doing stack spilling.
+ */
+int
+slp_make_initial_stub(PyThreadState *ts)
+{
+ if (ts->st.tealet_main == NULL) {
+ tealet_alloc_t ta = {
+ (tealet_malloc_t)&PyMem_Malloc,
+ (tealet_free_t)&PyMem_Free,
+ 0};
+ ts->st.tealet_main = tealet_initialize(&ta, sizeof(PyTealet_data));
+ if (!ts->st.tealet_main)
+ goto err;
+
+ }
+ if (ts->st.initial_stub) {
+ /* recreate stub, if it was deeper on the stack, otherwise, we just leave it */
+ /* note, this approach needs to be subject to tuning. Maybe the absolute position
+ * of the stub makes no difference. But for stack spilling, a "high" stub makes
+ * perfect sense.
+ */
+ void *a;
+ void *b;
+ a = tealet_get_far(ts->st.initial_stub);
+ b = slp_stub_far(ts->st.tealet_main, 0);
+ if (tealet_stack_diff(a, b) < 0) {
+ /* new stub will be higher up, recreate it */
+ tealet_delete(ts->st.initial_stub);
+ ts->st.initial_stub = NULL;
+ }
+ }
+
+ if (!ts->st.initial_stub) {
+ ts->st.initial_stub = slp_stub_new(ts->st.tealet_main, sizeof(PyTealet_data));
+ if (!ts->st.initial_stub)
+ goto err;
+#if 0 /* just to verify predicted pos */
+ {
+ void *c
+ c = slp_stub_far(ts->st.tealet_main, 0);
+ c = c;
+ }
+#endif
+ }
+ return 0;
+err:
+ PyErr_NoMemory();
+ return -1;
+}
+
+/* clean up tealet state for this thread */
+void slp_tealet_cleanup(PyThreadState *ts)
+{
+ slp_destroy_initial_stub(ts);
+ if (ts->st.tealet_main) {
+ /* we may be calling Py_Exit from a tasklet or otherwise
+ * from a sub-tealet. Therefore we can't make sanity checks
+ * like testing that we are the main tealet. Accept that we
+ * may leak suspended tealets in this case
+ */
+ if (TEALET_CURRENT_IS_MAIN(ts->st.tealet_main))
+ tealet_finalize(ts->st.tealet_main);
+ ts->st.tealet_main = NULL;
+ }
+}
+
+void
+slp_destroy_initial_stub(PyThreadState *ts)
+{
+ if (ts->st.initial_stub)
+ tealet_delete(ts->st.initial_stub);
+ ts->st.initial_stub = NULL;
+}
+
+/* The function that runs tasklet loop in a tealet */
+static tealet_t *tasklet_stub_func(tealet_t *me, void *arg)
+{
+ PyThreadState *ts = PyThreadState_GET();
+ PyFrameObject *f = ts->frame;
+ PyObject *result;
+ ts->frame = NULL;
+ ts->st.nesting_level = 0;
+ Py_CLEAR(ts->st.del_post_switch);
+ result = slp_run_tasklet(f);
+
+ /* this tealet is returning, which means that main is returning. Switch back
+ * to the main tealet. The result is passed to the target
+ */
+ tealet_exit(ts->st.tealet_main, (void*)result, TEALET_EXIT_DEFAULT);
+ /* this should never fail */
+ assert(0);
+ return NULL;
+}
+
+/* Running a function in the top level stub. If NULL is provided,
+ * use the tasklet evaluation loop
+ */
+
+/* Running a function in the top level stub */
+int
+slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg)
+{
+ tealet_t *stub;
+ int result;
+ stub = tealet_duplicate(ts->st.initial_stub, sizeof(PyTealet_data));
+ if (!stub) {
+ PyErr_NoMemory();
+ return -1;
+ }
+ result = slp_stub_run(stub, func, arg);
+ if (result)
+ return slp_tealet_error(result);
+ return 0;
+}
+
+/* call the top level loop as a means of starting a new such loop, hardswitching out
+ * of a tasklet. This invocation does not expect a return value
+ */
+int
+slp_run_stub_from_worker(PyThreadState *ts)
+{
+ return slp_run_initial_stub(ts, &tasklet_stub_func, NULL);
+}
+
+/* hard switching of tasklets */
+
+int
+slp_transfer(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev)
+{
+ int result;
+ int nesting_level;
+ tealet_t *current;
+
+ assert(prev->cstate == NULL);
+ nesting_level = prev->tstate->st.nesting_level;
+ prev->nesting_level = nesting_level;
+
+ /* mark the old tasklet as having cstate, and link it in */
+ current = tealet_current(ts->st.tealet_main);
+ prev->cstate = current;
+ slp_tealet_link(current, prev);
+
+ if (cst) {
+ /* make sure we are not trying to jump between threads */
+ assert(TEALET_RELATED(ts->st.tealet_main, cst));
+ result = tealet_switch(cst, NULL);
+ } else {
+ assert(TEALET_RELATED(ts->st.tealet_main, ts->st.initial_stub));
+ result = slp_run_stub_from_worker(ts);
+ }
+
+ /* we are back, or failed, no cstate in tasklet */
+ slp_tealet_unlink(current);
+ prev->cstate = NULL;
+ prev->tstate->st.nesting_level = nesting_level;
+ if (!result) {
+ if (ts->st.del_post_switch) {
+ PyObject *tmp;
+ TASKLET_CLAIMVAL(ts->st.current, &tmp);
+ Py_CLEAR(ts->st.del_post_switch);
+ TASKLET_SETVAL_OWN(ts->st.current, tmp);
+ }
+ return 0;
+ }
+ if (result == TEALET_ERR_MEM)
+ PyErr_NoMemory();
+ else
+ PyErr_SetString(PyExc_RuntimeError, "Invalid tasklet");
+ return -1;
+}
+
+int
+slp_transfer_return(tealet_t *cst)
+{
+ int result = tealet_exit(cst, NULL, TEALET_EXIT_DEFAULT);
+ if (result) {
+ /* emergency switch back to main tealet */
+ PyThreadState *ts = PyThreadState_GET();
+ PyErr_SetString(PyExc_RuntimeError, "Invalid tealet");
+ tealet_exit(ts->st.tealet_main, NULL, TEALET_EXIT_DEFAULT);
+ /* emergency!. Transfer to the _real_ main */
+ tealet_exit(TEALET_MAIN(ts->st.tealet_main), NULL, TEALET_EXIT_DEFAULT);
+ }
+ assert(0); /* never returns */
+ return 0;
+}
+
+int
+slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, PyTaskletObject *prev)
+{
+ int tracing = ts->tracing;
+ int use_tracing = ts->use_tracing;
+
+ Py_tracefunc c_profilefunc = ts->c_profilefunc;
+ Py_tracefunc c_tracefunc = ts->c_tracefunc;
+ PyObject *c_profileobj = ts->c_profileobj;
+ PyObject *c_traceobj = ts->c_traceobj;
+
+ PyObject *exc_type = ts->exc_type;
+ PyObject *exc_value = ts->exc_value;
+ PyObject *exc_traceback = ts->exc_traceback;
+ int ret;
+
+ ts->exc_type = ts->exc_value = ts->exc_traceback = NULL;
+ ts->c_profilefunc = ts->c_tracefunc = NULL;
+ ts->c_profileobj = ts->c_traceobj = NULL;
+ ts->use_tracing = ts->tracing = 0;
+
+ /* note that trace/profile are set without ref */
+ Py_XINCREF(c_profileobj);
+ Py_XINCREF(c_traceobj);
+
+ ret = slp_transfer(ts, cst, prev);
+
+ ts->tracing = tracing;
+ ts->use_tracing = use_tracing;
+
+ ts->c_profilefunc = c_profilefunc;
+ ts->c_tracefunc = c_tracefunc;
+ ts->c_profileobj = c_profileobj;
+ ts->c_traceobj = c_traceobj;
+ Py_XDECREF(c_profileobj);
+ Py_XDECREF(c_traceobj);
+
+ ts->exc_type = exc_type;
+ ts->exc_value = exc_value;
+ ts->exc_traceback = exc_traceback;
+ return ret;
+}
+
+#ifndef CSTACK_WATERMARK
+#define CSTACK_WATERMARK 16384
+#endif
+int slp_cstack_save_now(PyThreadState *ts)
+{
+ void *a, *b;
+ ptrdiff_t diff;
+ if (!ts->st.initial_stub)
+ return 0;
+ assert(ts->st.initial_stub);
+ a = tealet_get_far(ts->st.initial_stub);
+ b = slp_stub_far(NULL, 0);
+ diff = tealet_stack_diff(a, b);
+#if 0 /* enable this for testing, a low threshold */
+ return diff > 1000;
+#endif
+ return diff > CSTACK_WATERMARK;
+}
\ No newline at end of file
diff --git a/Stackless/core/stackless_tealet.h b/Stackless/core/stackless_tealet.h
new file mode 100644
index 00000000000000..1baf871ba5fc64
--- /dev/null
+++ b/Stackless/core/stackless_tealet.h
@@ -0,0 +1,33 @@
+/* interface to the stack switching routines used by stackless */
+#ifndef _STACKLESS_TEALET_H
+#define _STACKLESS_TEALET_H
+
+#include
+#include
+
+/* a list of tealets. Currently these are suspended tealets with their
+ * suspended tasklets indicated.
+ */
+typedef struct PyTealet_data {
+ struct PyTealet_data *prev;
+ struct PyTealet_data *next;
+ struct PyTaskletObject *tasklet;
+} PyTealet_data;
+
+PyTealet_data *slp_tealet_list; /* head of the tealet list */
+
+int slp_make_initial_stub(PyThreadState *ts);
+void slp_destroy_initial_stub(PyThreadState *ts);
+int slp_run_initial_stub(PyThreadState *ts, tealet_run_t func, void **arg);
+int slp_run_stub_from_worker(PyThreadState *ts);
+void slp_tealet_cleanup(PyThreadState *ts);
+
+/* hard switching of tasklets */
+int slp_transfer(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev);
+int slp_transfer_with_exc(PyThreadState *ts, tealet_t *cst, struct PyTaskletObject *prev);
+int slp_transfer_return(tealet_t *cst);
+
+/* stack spilling */
+int slp_cstack_save_now(PyThreadState *ts);
+
+#endif
\ No newline at end of file
diff --git a/Stackless/core/stackless_tstate.h b/Stackless/core/stackless_tstate.h
index c0fc2f12c249e7..6eb3c14c48fbac 100644
--- a/Stackless/core/stackless_tstate.h
+++ b/Stackless/core/stackless_tstate.h
@@ -1,29 +1,12 @@
/*** addition to tstate ***/
typedef struct _sts {
- /* the blueprint for new stacks */
- struct _cstack *initial_stub;
- /* "serial" is incremented each time we create a new stub.
- * (enter "stackless" from the outside)
- * and "serial_last_jump" indicates to which stub the current
- * stack belongs. This is used to find out if a stack switch
- * is required when the main tasklet exits
- */
-#ifdef have_long_long
- long_long serial;
- long_long serial_last_jump;
-#else
- long serial;
- long serial_last_jump;
-#endif
- /* the base address for hijacking stacks. XXX deprecating */
- intptr_t *cstack_base;
- /* stack overflow check and init flag */
- intptr_t *cstack_root;
+ struct tealet_t *initial_stub; /* stub for creating new tealets */
+ struct tealet_t *tealet_main; /* the current "main" tealet */
/* main tasklet */
- struct _tasklet *main;
+ struct PyTaskletObject *main;
/* runnable tasklets */
- struct _tasklet *current;
+ struct PyTaskletObject *current;
int runcount;
/* scheduling */
@@ -53,10 +36,7 @@ typedef struct _sts {
/* these macros go into pystate.c */
#define __STACKLESS_PYSTATE_NEW \
tstate->st.initial_stub = NULL; \
- tstate->st.serial = 0; \
- tstate->st.serial_last_jump = 0; \
- tstate->st.cstack_base = NULL; \
- tstate->st.cstack_root = NULL; \
+ tstate->st.tealet_main = NULL; \
tstate->st.ticker = 0; \
tstate->st.interval = 0; \
tstate->st.interrupt = NULL; \
@@ -79,12 +59,13 @@ typedef struct _sts {
struct _ts; /* Forward */
void slp_kill_tasks_with_stacks(struct _ts *tstate);
+void slp_tealet_cleanup(struct _ts *tstate);
#define __STACKLESS_PYSTATE_CLEAR \
slp_kill_tasks_with_stacks(tstate); \
- Py_CLEAR(tstate->st.initial_stub); \
Py_CLEAR(tstate->st.del_post_switch); \
- Py_CLEAR(tstate->st.interrupted);
+ Py_CLEAR(tstate->st.interrupted); \
+ slp_tealet_cleanup(tstate);
#ifdef WITH_THREAD
diff --git a/Stackless/core/stacklesseval.c b/Stackless/core/stacklesseval.c
index fe7a77959ebb41..ec330be2574fac 100644
--- a/Stackless/core/stacklesseval.c
+++ b/Stackless/core/stacklesseval.c
@@ -8,8 +8,7 @@
#include "stackless_impl.h"
#include "pickling/prickelpit.h"
-/* platform specific constants */
-#include "platf/slp_platformselect.h"
+#include "stackless_tealet.h"
/* Stackless extension for ceval.c */
@@ -37,8 +36,8 @@ int slp_in_psyco = 0;
*/
int slp_try_stackless = 0;
-/* the list of all stacks of all threads */
-struct _cstack *slp_cstack_chain = NULL;
+/* the list of all tasklets of all threads */
+PyTaskletObject *slp_tasklet_chain = NULL;
/******************************************************
@@ -47,232 +46,11 @@ struct _cstack *slp_cstack_chain = NULL;
******************************************************/
-static PyCStackObject *cstack_cache[CSTACK_SLOTS] = { NULL };
-static int cstack_cachecount = 0;
/* this function will get called by PyStacklessEval_Fini */
-static void slp_cstack_cacheclear(void)
-{
- int i;
- PyCStackObject *stack;
-
- for (i=0; i < CSTACK_SLOTS; i++) {
- while (cstack_cache[i] != NULL) {
- stack = cstack_cache[i];
- cstack_cache[i] = (PyCStackObject *) stack->startaddr;
- PyObject_Del(stack);
- }
- }
- cstack_cachecount = 0;
-}
-
-static void
-cstack_dealloc(PyCStackObject *cst)
-{
- slp_cstack_chain = cst;
- SLP_CHAIN_REMOVE(PyCStackObject, &slp_cstack_chain, cst, next,
- prev);
- if (cst->ob_size >= CSTACK_SLOTS) {
- PyObject_Del(cst);
- }
- else {
- if (cstack_cachecount >= CSTACK_MAXCACHE)
- slp_cstack_cacheclear();
- cst->startaddr = (intptr_t *) cstack_cache[cst->ob_size];
- cstack_cache[cst->ob_size] = cst;
- ++cstack_cachecount;
- }
-}
-
-
-PyCStackObject *
-slp_cstack_new(PyCStackObject **cst, intptr_t *stackref, PyTaskletObject *task)
-{
- PyThreadState *ts = PyThreadState_GET();
- intptr_t *stackbase = ts->st.cstack_base;
- ptrdiff_t size = stackbase - stackref;
-
- assert(size >= 0);
-
- if (*cst != NULL) {
- if ((*cst)->task == task)
- (*cst)->task = NULL;
- Py_DECREF(*cst);
- }
- if (size < CSTACK_SLOTS && ((*cst) = cstack_cache[size])) {
- /* take stack from cache */
- cstack_cache[size] = (PyCStackObject *) (*cst)->startaddr;
- --cstack_cachecount;
- _Py_NewReference((PyObject *)(*cst));
- }
- else
- *cst = PyObject_NewVar(PyCStackObject, &PyCStack_Type, size);
- if (*cst == NULL) return NULL;
-
- (*cst)->startaddr = stackbase;
- (*cst)->next = (*cst)->prev = NULL;
- SLP_CHAIN_INSERT(PyCStackObject, &slp_cstack_chain, *cst, next, prev);
- (*cst)->serial = ts->st.serial_last_jump;
- (*cst)->task = task;
- (*cst)->tstate = ts;
- (*cst)->nesting_level = ts->st.nesting_level;
-#ifdef _SEH32
- //save the SEH handler
- (*cst)->exception_list = (DWORD)
- __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList));
-#endif
- return *cst;
-}
-
-size_t
-slp_cstack_save(PyCStackObject *cstprev)
-{
- size_t stsizeb = (cstprev)->ob_size * sizeof(intptr_t);
-
- memcpy((cstprev)->stack, (cstprev)->startaddr -
- (cstprev)->ob_size, stsizeb);
-#ifdef _SEH32
- //save the SEH handler
- cstprev->exception_list = (DWORD)
- __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList));
-#endif
- return stsizeb;
-}
-
-void
-#ifdef _SEH32
-#pragma warning(disable:4733) /* disable warning about modifying FS[0] */
-#endif
-slp_cstack_restore(PyCStackObject *cst)
-{
- cst->tstate->st.nesting_level = cst->nesting_level;
- /* mark task as no longer responsible for cstack instance */
- cst->task = NULL;
- memcpy(cst->startaddr - cst->ob_size, &cst->stack,
- (cst->ob_size) * sizeof(intptr_t));
-#ifdef _SEH32
- //restore the SEH handler
- __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), (DWORD)(cst->exception_list));
- #pragma warning(default:4733)
-#endif
-}
-
-
-static char cstack_doc[] =
-"A CStack object serves to save the stack slice which is involved\n\
-during a recursive Python call. It will also be used for pickling\n\
-of program state. This structure is highly platform dependant.\n\
-Note: For inspection, str() can dump it as a string.\
-";
-
-#if SIZEOF_VOIDP == SIZEOF_INT
-#define T_ADDR T_UINT
-#else
-#define T_ADDR T_ULONG
-#endif
-
-static PyMemberDef cstack_members[] = {
- {"size", T_INT, offsetof(PyCStackObject, ob_size), READONLY},
- {"next", T_OBJECT, offsetof(PyCStackObject, next), READONLY},
- {"prev", T_OBJECT, offsetof(PyCStackObject, prev), READONLY},
- {"task", T_OBJECT, offsetof(PyCStackObject, task), READONLY},
- {"startaddr", T_ADDR, offsetof(PyCStackObject, startaddr), READONLY},
- {0}
-};
-/* simple string interface for inspection */
-static PyObject *
-cstack_str(PyObject *o)
-{
- PyCStackObject *cst = (PyCStackObject*)o;
- return PyString_FromStringAndSize((char*)&cst->stack,
- cst->ob_size*sizeof(cst->stack[0]));
-}
-
-PyTypeObject PyCStack_Type = {
- PyObject_HEAD_INIT(&PyType_Type)
- 0,
- "stackless.cstack",
- sizeof(PyCStackObject),
- sizeof(PyObject *),
- (destructor)cstack_dealloc, /* tp_dealloc */
- 0, /* tp_print */
- 0, /* tp_getattr */
- 0, /* tp_setattr */
- 0, /* tp_compare */
- 0, /* tp_repr */
- 0, /* tp_as_number */
- 0, /* tp_as_sequence */
- 0, /* tp_as_mapping */
- 0, /* tp_hash */
- 0, /* tp_call */
- (reprfunc)cstack_str, /* tp_str */
- PyObject_GenericGetAttr, /* tp_getattro */
- PyObject_GenericSetAttr, /* tp_setattro */
- 0, /* tp_as_buffer */
- Py_TPFLAGS_DEFAULT, /* tp_flags */
- cstack_doc, /* tp_doc */
- 0, /* tp_traverse */
- 0, /* tp_clear */
- 0, /* tp_richcompare */
- 0, /* tp_weaklistoffset */
- 0, /* tp_iter */
- 0, /* tp_iternext */
- 0, /* tp_methods */
- cstack_members, /* tp_members */
-};
-
-
-static int
-make_initial_stub(void)
-{
- PyThreadState *ts = PyThreadState_GET();
- int result;
-
- if (ts->st.initial_stub != NULL) {
- Py_DECREF(ts->st.initial_stub);
- ts->st.initial_stub = NULL;
- }
- ts->st.serial_last_jump = ++ts->st.serial;
- result = slp_transfer(&ts->st.initial_stub, NULL, NULL);
- if (result < 0)
- return result;
- /*
- * from here, we always arrive with a compatible cstack
- * that also can be used by main, if it is running
- * in soft-switching mode.
- * To insure that, it was necessary to re-create the
- * initial stub for *every* run of a new main.
- * This will vanish with greenlet-like stack management.
- */
-
- return result;
-}
-
-static PyObject *
-climb_stack_and_eval_frame(PyFrameObject *f)
-{
- /*
- * a similar case to climb_stack_and_transfer,
- * but here we need to incorporate a gap in the
- * stack into main and keep this gap on the stack.
- * This way, initial_stub is always valid to be
- * used to return to the main c stack.
- */
- PyThreadState *ts = PyThreadState_GET();
- intptr_t probe;
- ptrdiff_t needed = &probe - ts->st.cstack_base;
- /* in rare cases, the need might have vanished due to the recursion */
- intptr_t *goobledigoobs;
- if (needed > 0) {
- goobledigoobs = alloca(needed * sizeof(intptr_t));
- if (goobledigoobs == NULL)
- return NULL;
- }
- return slp_eval_frame(f);
-}
PyObject *
@@ -280,42 +58,19 @@ slp_eval_frame(PyFrameObject *f)
{
PyThreadState *ts = PyThreadState_GET();
PyFrameObject *fprev = f->f_back;
- intptr_t * stackref;
if (fprev == NULL && ts->st.main == NULL) {
- int returning;
- /* this is the initial frame, so mark the stack base */
-
- /*
- * careful, this caused me a major headache.
- * it is *not* sufficient to just check for fprev == NULL.
- * Reason: (observed with wxPython):
- * A toplevel frame is run as a tasklet. When its frame
- * is deallocated (in tasklet_end), a Python object
- * with a __del__ method is destroyed. This __del__
- * will run as a toplevel frame, with f_back == NULL!
+ /* this is the initial frame. From here we must
+ * run the evaluation loop on a separate tealet, so that
+ * all such tealets are equivalent and can jump back to main
+ * when they exit.
*/
-
- stackref = STACK_REFPLUS + (intptr_t *) &f;
- if (ts->st.cstack_base == NULL)
- ts->st.cstack_base = stackref - CSTACK_GOODGAP;
- if (stackref > ts->st.cstack_base)
- return climb_stack_and_eval_frame(f);
-
- returning = make_initial_stub();
- if (returning < 0)
+ PyObject *result;
+ if (slp_make_initial_stub(ts))
return NULL;
- /* returning will be 1 if we "switched back" to this stub, and 0
- * if this is the original call that just created the stub.
- * If the stub is being reused, the argument, i.e. the frame,
- * is in ts->frame
- */
- if (returning == 1) {
- f = ts->frame;
- ts->frame = NULL;
- }
return slp_run_tasklet(f);
}
+
Py_INCREF(Py_None);
return slp_frame_dispatch(f, fprev, 0, Py_None);
}
@@ -327,32 +82,29 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
/* a loop to kill tasklets on the local thread */
while (1) {
- PyCStackObject *csfirst = slp_cstack_chain, *cs;
+ PyTealet_data *tdfirst = slp_tealet_list, *td;
PyTaskletObject *t;
- if (csfirst == NULL)
+ if (tdfirst == NULL)
break;
- for (cs = csfirst; ; cs = cs->next) {
- if (count && cs == csfirst) {
+ for (td = tdfirst; ; td = td->next) {
+ if (count && td == tdfirst) {
/* nothing found */
return;
}
++count;
- if (cs->task == NULL)
+ if (td->tasklet == NULL)
continue;
/* can't kill tasklets from other threads here */
- if (cs->tstate != ts)
- continue;
- /* were we asked to kill tasklet on our thread? */
- if (target_ts != NULL && cs->tstate != target_ts)
+ if (td->tasklet->tstate != ts)
continue;
/* Not killable, another thread's frameless main? */
- if (slp_get_frame(cs->task) == NULL)
+ if (slp_get_frame(td->tasklet) == NULL)
continue;
break;
}
count = 0;
- t = cs->task;
+ t = td->tasklet;
Py_INCREF(t); /* cs->task is a borrowed ref */
/* We need to ensure that the tasklet 't' is in the scheduler
@@ -365,7 +117,7 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
* killed, they will be implicitly placed before this one,
* leaving it to run next.
*/
- if (!t->flags.blocked && t != cs->tstate->st.current) {
+ if (!t->flags.blocked && t != t->tstate->st.current) {
PyTaskletObject *tmp;
PyTaskletObject **chain;
/* unlink from runnable queue if it wasn't previously remove()'d */
@@ -373,12 +125,12 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
assert(t->prev);
chain = &t;
SLP_CHAIN_REMOVE(PyTaskletObject, chain, tmp, next, prev);
- ts->st.runcount--;
t = tmp;
+ ts->st.runcount--;
} else
Py_INCREF(t); /* a new reference for the runnable queue */
/* insert into the 'current' chain without modifying 'current' */
- tmp = cs->tstate->st.current;
+ tmp = t->tstate->st.current;
chain = &tmp;
SLP_CHAIN_INSERT(PyTaskletObject, chain, t, next, prev);
ts->st.runcount++;
@@ -387,10 +139,6 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
PyTasklet_Kill(t);
PyErr_Clear();
- if (t->cstate != NULL) {
- /* ensure a valid tstate */
- t->cstate->tstate = slp_initial_tstate;
- }
Py_DECREF(t);
}
@@ -400,18 +148,24 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
* exit this function
*/
{
- PyCStackObject *csfirst = slp_cstack_chain, *cs;
+ PyTealet_data *tdfirst = slp_tealet_list, *td;
PyTaskletObject *t;
- if (csfirst == NULL)
+ if (tdfirst == NULL)
return;
count = 0;
- for (cs = csfirst; ; cs = cs->next) {
- if (count && cs == csfirst) {
+ for (td = tdfirst; ; td = td->next) {
+ if (count && td == tdfirst) {
return;
}
+ if (td->tasklet == NULL)
+ continue;
+ t = td->tasklet;
+ if (t->tstate == ts)
+ continue; /* ignore this thread's tasklets */
+ if (target_ts && t->tstate != target_ts)
+ continue; /* want a specific thread */
count++;
- t = cs->task;
Py_INCREF(t); /* cs->task is a borrowed ref */
PyTasklet_Kill(t);
PyErr_Clear();
@@ -423,6 +177,9 @@ void slp_kill_tasks_with_stacks(PyThreadState *target_ts)
void PyStackless_kill_tasks_with_stacks(int allthreads)
{
PyThreadState *ts = PyThreadState_Get();
+ int init = 0;
+ if (slp_tealet_list == NULL)
+ return;
if (ts->st.main == NULL) {
if (initialize_main_and_current()) {
@@ -431,118 +188,72 @@ void PyStackless_kill_tasks_with_stacks(int allthreads)
Py_XDECREF(s);
return;
}
+ init = 1;
}
slp_kill_tasks_with_stacks(allthreads ? NULL : ts);
+
+ if (init) {
+ PyTaskletObject *m = ts->st.main, *c;
+ ts->st.main = NULL;
+ c = slp_current_remove();
+ Py_XDECREF(m);
+ Py_XDECREF(c);
+ }
}
/* cstack spilling for recursive calls */
-
-static PyObject *
-eval_frame_callback(PyFrameObject *f, int exc, PyObject *retval)
+typedef struct eval_args
{
- PyThreadState *ts = PyThreadState_GET();
- PyTaskletObject *cur = ts->st.current;
- PyCStackObject *cst;
- PyCFrameObject *cf = (PyCFrameObject *) f;
- intptr_t *saved_base;
-
- //make sure we don't try softswitching out of this callstack
- ts->st.nesting_level = cf->n + 1;
- ts->frame = f->f_back;
-
- //this tasklet now runs in this tstate.
- cst = cur->cstate; //The calling cstate
- cur->cstate = ts->st.initial_stub;
- Py_INCREF(cur->cstate);
-
- /* We must base our new stack from here, because otherwise we might find
- * ourselves in an infinite loop of stack spilling.
- */
- saved_base = ts->st.cstack_root;
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f;
-
- /* pull in the right retval and tempval from the arguments */
- Py_DECREF(retval);
- retval = cf->ob1;
- cf->ob1 = NULL;
- TASKLET_SETVAL_OWN(cur, cf->ob2);
- cf->ob2 = NULL;
+ PyThreadState *ts;
+ tealet_t *return_to;
+ PyFrameObject *f;
+ int exc;
+ PyObject *retval;
+} eval_args;
- retval = PyEval_EvalFrameEx_slp(ts->frame, exc, retval);
- ts->st.cstack_root = saved_base;
-
- /* store retval back into the cstate object */
- if (retval == NULL)
- retval = slp_curexc_to_bomb();
- if (retval == NULL)
- goto fatal;
- cf->ob1 = retval;
-
- /* jump back */
- Py_DECREF(cur->cstate);
- cur->cstate = cst;
- slp_transfer_return(cst);
- /* should never come here */
-fatal:
- Py_DECREF(cf); /* since the caller won't do it */
- return NULL;
+static tealet_t *
+eval_frame_callback(tealet_t *current, void *arg)
+{
+ eval_args *args = (eval_args*)arg;
+ PyThreadState *ts = args->ts;
+
+ /*make sure we don't try softswitching out of this callstack
+ /* TODO: find a way to keep this tasklet pickleable, but not softswitchable */
+ ts->st.nesting_level++;
+
+ /* perform the call */
+ args->retval = PyEval_EvalFrameEx_slp(args->f, args->exc, args->retval);
+ ts->st.nesting_level--;
+ return args->return_to;
}
PyObject *
slp_eval_frame_newstack(PyFrameObject *f, int exc, PyObject *retval)
{
PyThreadState *ts = PyThreadState_GET();
- PyTaskletObject *cur = ts->st.current;
- PyCFrameObject *cf = NULL;
- PyCStackObject *cst;
-
- if (cur == NULL || PyErr_Occurred()) {
- /* Bypass this during early initialization or if we have a pending
- * exception, such as the one set via gen_close(). Doing the stack
- * magic here will clear that exception.
- */
- intptr_t *old = ts->st.cstack_root;
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f;
- retval = PyEval_EvalFrameEx_slp(f,exc, retval);
- ts->st.cstack_root = old;
- return retval;
- }
- if (ts->st.cstack_root == NULL) {
- /* this is a toplevel call. Store the root of stack spilling */
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f;
- retval = PyEval_EvalFrameEx_slp(f, exc, retval);
- /* and reset it. We may reenter stackless at a completely different
- * depth later
- */
- ts->st.cstack_root = NULL;
- return retval;
+ eval_args *args = PyMem_MALLOC(sizeof(eval_args));
+ int result;
+ void *argp;
+ if (!args) {
+ PyErr_NoMemory();
+ return NULL;
}
-
- ts->frame = f;
- cf = slp_cframe_new(eval_frame_callback, 1);
- if (cf == NULL)
+ args->ts = ts;
+ args->return_to = tealet_current(ts->st.tealet_main);
+ args->f = f;
+ args->exc = exc;
+ args->retval = retval;
+
+ argp = (void*)args;
+ result = slp_run_initial_stub(ts, eval_frame_callback, &argp);
+ if (result) {
+ Py_XDECREF(retval);
+ PyMem_FREE(args);
return NULL;
- cf->n = ts->st.nesting_level;
- cf->ob1 = retval;
- /* store the tmpval here so that it won't get clobbered
- * by slp_run_tasklet()
- */
- TASKLET_CLAIMVAL(cur, &(cf->ob2));
- ts->frame = (PyFrameObject *) cf;
- cst = cur->cstate;
- cur->cstate = NULL;
- if (slp_transfer(&cur->cstate, NULL, cur) < 0)
- goto finally; /* fatal */
- Py_XDECREF(cur->cstate);
-
- retval = cf->ob1;
- cf->ob1 = NULL;
- if (PyBomb_Check(retval))
- retval = slp_bomb_explode(retval);
-finally:
- Py_DECREF(cf);
- cur->cstate = cst;
+ }
+ retval = args->retval;
+ PyMem_FREE(args);
return retval;
}
@@ -817,7 +528,6 @@ slp_frame_dispatch_top(PyObject *retval)
void
slp_stacklesseval_fini(void)
{
- slp_cstack_cacheclear();
}
#endif /* STACKLESS */
diff --git a/Stackless/module/channelobject.c b/Stackless/module/channelobject.c
index 17e3c9c796e2c8..e2541becc90a76 100644
--- a/Stackless/module/channelobject.c
+++ b/Stackless/module/channelobject.c
@@ -450,7 +450,7 @@ generic_channel_action(PyChannelObject *self, PyObject *arg, int dir, int stackl
PyTaskletObject *source = ts->st.current;
PyTaskletObject *target = self->head;
int cando = dir > 0 ? self->balance < 0 : self->balance > 0;
- int interthread = cando ? target->cstate->tstate != ts : 0;
+ int interthread = cando ? target->tstate != ts : 0;
PyObject *tmpval, *retval;
int fail;
@@ -493,7 +493,7 @@ generic_channel_cando(PyThreadState *ts, PyObject **result, PyChannelObject *sel
/* swap data and perform necessary scheduling */
switchto = target = slp_channel_remove(self, NULL, NULL, &next);
- interthread = target->cstate->tstate != ts;
+ interthread = target->tstate != ts;
/* exchange data */
TASKLET_SWAPVAL(source, target);
diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c
index a74474f2908b94..9e8b71d96f96e7 100644
--- a/Stackless/module/scheduling.c
+++ b/Stackless/module/scheduling.c
@@ -2,6 +2,7 @@
#ifdef STACKLESS
#include "core/stackless_impl.h"
+#include "core/stackless_tealet.h"
#ifdef WITH_THREAD
#include "pythread.h"
@@ -330,51 +331,6 @@ PyTypeObject PyBomb_Type = {
slp_schedule_hook_func *_slp_schedule_fasthook;
PyObject *_slp_schedule_hook;
-static int
-transfer_with_exc(PyCStackObject **cstprev, PyCStackObject *cst, PyTaskletObject *prev)
-{
- PyThreadState *ts = PyThreadState_GET();
-
- int tracing = ts->tracing;
- int use_tracing = ts->use_tracing;
-
- Py_tracefunc c_profilefunc = ts->c_profilefunc;
- Py_tracefunc c_tracefunc = ts->c_tracefunc;
- PyObject *c_profileobj = ts->c_profileobj;
- PyObject *c_traceobj = ts->c_traceobj;
-
- PyObject *exc_type = ts->exc_type;
- PyObject *exc_value = ts->exc_value;
- PyObject *exc_traceback = ts->exc_traceback;
- int ret;
-
- ts->exc_type = ts->exc_value = ts->exc_traceback = NULL;
- ts->c_profilefunc = ts->c_tracefunc = NULL;
- ts->c_profileobj = ts->c_traceobj = NULL;
- ts->use_tracing = ts->tracing = 0;
-
- /* note that trace/profile are set without ref */
- Py_XINCREF(c_profileobj);
- Py_XINCREF(c_traceobj);
-
- ret = slp_transfer(cstprev, cst, prev);
-
- ts->tracing = tracing;
- ts->use_tracing = use_tracing;
-
- ts->c_profilefunc = c_profilefunc;
- ts->c_tracefunc = c_tracefunc;
- ts->c_profileobj = c_profileobj;
- ts->c_traceobj = c_traceobj;
- Py_XDECREF(c_profileobj);
- Py_XDECREF(c_traceobj);
-
- ts->exc_type = exc_type;
- ts->exc_value = exc_value;
- ts->exc_traceback = exc_traceback;
- return ret;
-}
-
/* scheduler monitoring */
int
@@ -525,17 +481,6 @@ jump_soft_to_hard(PyFrameObject *f, int exc, PyObject *retval)
/* combined soft/hard switching */
-int
-slp_ensure_linkage(PyTaskletObject *t)
-{
- if (t->cstate->task == t)
- return 0;
- if (!slp_cstack_new(&t->cstate, t->cstate->tstate->st.cstack_base, t))
- return -1;
- t->cstate->nesting_level = 0;
- return 0;
-}
-
/* check whether a different thread can be run */
@@ -749,7 +694,7 @@ schedule_task_interthread(PyObject **result,
int stackless,
int *did_switch)
{
- PyThreadState *nts = next->cstate->tstate;
+ PyThreadState *nts = next->tstate;
int fail;
/* get myself ready, since the previous task is going to continue on the
@@ -846,8 +791,7 @@ slp_schedule_task(PyObject **result, PyTaskletObject *prev, PyTaskletObject *nex
return schedule_task_block(result, prev, stackless, did_switch);
#ifdef WITH_THREAD
- /* note that next->cstate is undefined if it is ourself */
- if (next->cstate != NULL && next->cstate->tstate != ts) {
+ if (next->tstate != ts) {
return schedule_task_interthread(result, prev, next, stackless, did_switch);
}
#endif
@@ -891,10 +835,8 @@ static int
slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject *prev, PyTaskletObject *next, int stackless,
int *did_switch)
{
- PyCStackObject **cstprev;
-
PyObject *retval;
- int (*transfer)(PyCStackObject **, PyCStackObject *, PyTaskletObject *);
+ int (*transfer)(PyThreadState *, tealet_t *, PyTaskletObject *);
/* remove the no-soft-irq flag from the runflags */
int no_soft_irq = ts->st.runflags & PY_WATCHDOG_NO_SOFT_IRQ;
@@ -920,18 +862,16 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject
if (!stackless || ts->st.nesting_level != 0)
goto hard_switching;
- /* start of soft switching code */
+ /* We never soft switch out of the main tasklet. Otherwise, we might end up soft switching
+ * back to the mani tasklet from another stack, but that screws up with assumptions
+ * of the caller. e.g. the main tasklet might make callbacks back into the program, but
+ * it really shouln't do that from a different stack. For example, the main stack might
+ * have structured exception handlers installed.
+ */
+ if (prev == ts->st.main)
+ goto hard_switching;
- if (prev->cstate != ts->st.initial_stub) {
- Py_DECREF(prev->cstate);
- prev->cstate = ts->st.initial_stub;
- Py_INCREF(prev->cstate);
- }
- if (ts != slp_initial_tstate) {
- /* ensure to get all tasklets into the other thread's chain */
- if (slp_ensure_linkage(prev) || slp_ensure_linkage(next))
- return -1;
- }
+ /* start of soft switching code */
/* handle exception */
if (ts->exc_type == Py_None) {
@@ -973,9 +913,8 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject
ts->c_traceobj = ts->c_profileobj = NULL;
ts->tracing = ts->use_tracing = 0;
}
- assert(next->cstate != NULL);
- if (next->cstate->nesting_level != 0) {
+ if (next->cstate) {
/* can soft switch out of this tasklet, but the target tasklet
* was in a hard switched state, so we need a helper frame to
* jump to the destination stack
@@ -1021,9 +960,8 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject
/* since we change the stack we must assure that the protocol was met */
STACKLESS_ASSERT();
- /* note: nesting_level is handled in cstack_new */
- cstprev = &prev->cstate;
-
+ /* note: nesting_level is handled in slp_transfer */
+
ts->st.current = next;
if (ts->exc_type == Py_None) {
@@ -1036,11 +974,11 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject
++ts->st.nesting_level;
if (ts->exc_type != NULL || ts->use_tracing || ts->tracing)
- transfer = transfer_with_exc;
+ transfer = slp_transfer_with_exc;
else
transfer = slp_transfer;
- if (transfer(cstprev, next->cstate, prev) >= 0) {
+ if (transfer(ts, next->cstate, prev) >= 0) {
--ts->st.nesting_level;
TASKLET_CLAIMVAL(prev, &retval);
if (PyBomb_Check(retval))
@@ -1086,7 +1024,9 @@ initialize_main_and_current(void)
&PyTasklet_Type, noargs, NULL);
Py_DECREF(noargs);
if (task == NULL) return -1;
- assert(task->cstate != NULL);
+ task->cstate = NULL;
+ task->tstate = ts;
+ task->nesting_level = 0;
ts->st.main = task;
Py_INCREF(task);
slp_current_insert(task);
@@ -1213,17 +1153,6 @@ tasklet_end(PyObject *retval)
*/
TASKLET_SETVAL(task, retval);
- if (ismain) {
- /*
- * Because of soft switching, we may find ourself in the top level of a stack that was created
- * using another stub (another entry point into stackless). If so, we need a final return to
- * the original stub if necessary. (Meanwhile, task->cstate may be an old nesting state and not
- * the original stub, so we take the stub from the tstate)
- */
- if (ts->st.serial_last_jump != ts->st.initial_stub->serial)
- slp_transfer_return(ts->st.initial_stub);
- }
-
/* remove from runnables */
slp_current_remove();
diff --git a/Stackless/module/stacklessmodule.c b/Stackless/module/stacklessmodule.c
index 7d2ea7dd5cd672..9f9bd563fc20d9 100644
--- a/Stackless/module/stacklessmodule.c
+++ b/Stackless/module/stacklessmodule.c
@@ -6,7 +6,6 @@
#include "core/stackless_impl.h"
#define IMPLEMENT_STACKLESSMODULE
-#include "platf/slp_platformselect.h"
#include "core/cframeobject.h"
#include "taskletobject.h"
#include "channelobject.h"
@@ -659,6 +658,9 @@ This can be used to measure the execution time of 1.000.000 switches.");
#define STACK_MAX_USEFUL 64000
#define STACK_MAX_USESTR "64000"
+#ifdef MS_WINDOWS
+#define alloca _alloca
+#endif
static
PyObject *
@@ -705,13 +707,12 @@ PyObject *
test_outside(PyObject *self)
{
PyThreadState *ts = PyThreadState_GET();
- PyTaskletObject *stmain = ts->st.main;
- PyCStackObject *cst = ts->st.initial_stub;
+ PyTaskletObject *stcurrent, *stmain = ts->st.main;
+ tealet_t *cst = ts->st.initial_stub;
PyFrameObject *f = ts->frame;
int recursion_depth = ts->recursion_depth;
int nesting_level = ts->st.nesting_level;
PyObject *ret = Py_None;
- int jump = ts->st.serial_last_jump;
Py_INCREF(ret);
ts->st.main = NULL;
@@ -719,6 +720,7 @@ test_outside(PyObject *self)
ts->st.nesting_level = 0;
ts->frame = NULL;
ts->recursion_depth = 0;
+ stcurrent = ts->st.current;
slp_current_remove();
while (ts->st.runcount > 0) {
Py_DECREF(ret);
@@ -728,13 +730,12 @@ test_outside(PyObject *self)
}
}
ts->st.main = stmain;
- Py_XDECREF(ts->st.initial_stub);
+ slp_destroy_initial_stub(ts);
ts->st.initial_stub = cst;
ts->frame = f;
- slp_current_insert(stmain);
+ slp_current_insert(stcurrent);
ts->recursion_depth = recursion_depth;
ts->st.nesting_level = nesting_level;
- ts->st.serial_last_jump = jump;
return ret;
}
@@ -978,7 +979,6 @@ PyStackless_CallMethod_Main(PyObject *o, char *name, char *format, ...)
return NULL;
}
} else {
- /* direct call, no method lookup */
func = o;
Py_INCREF(func);
}
@@ -1396,19 +1396,21 @@ static PyObject *
slpmodule_getuncollectables(PySlpModuleObject *mod, void *context)
{
PyObject *lis = PyList_New(0);
- PyCStackObject *cst = slp_cstack_chain;
-
+ PyTealet_data *data = slp_tealet_list;
+
if (lis == NULL)
return NULL;
- do {
- if (cst->task != NULL) {
- if (PyList_Append(lis, (PyObject *) cst->task)) {
- Py_DECREF(lis);
- return NULL;
+ if (data) {
+ do {
+ if (data->tasklet) {
+ if (PyList_Append(lis, (PyObject*)data->tasklet)) {
+ Py_DECREF(lis);
+ return NULL;
+ }
}
- }
- cst = cst->next;
- } while (cst != slp_cstack_chain);
+ data = data->next;
+ } while (data != slp_tealet_list);
+ }
return lis;
}
@@ -1604,7 +1606,6 @@ _PyStackless_Init(void)
INSERT("slpmodule", PySlpModule_TypePtr);
INSERT("cframe", &PyCFrame_Type);
- INSERT("cstack", &PyCStack_Type);
INSERT("bomb", &PyBomb_Type);
INSERT("tasklet", &PyTasklet_Type);
INSERT("channel", &PyChannel_Type);
diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c
index 9b9dcff0fabe8a..13c739c732388c 100644
--- a/Stackless/module/taskletobject.c
+++ b/Stackless/module/taskletobject.c
@@ -13,7 +13,7 @@
void
slp_current_insert(PyTaskletObject *task)
{
- PyThreadState *ts = task->cstate->tstate;
+ PyThreadState *ts = task->tstate;
PyTaskletObject **chain = &ts->st.current;
SLP_CHAIN_INSERT(PyTaskletObject, chain, task, next, prev);
@@ -23,7 +23,7 @@ slp_current_insert(PyTaskletObject *task)
void
slp_current_insert_after(PyTaskletObject *task)
{
- PyThreadState *ts = task->cstate->tstate;
+ PyThreadState *ts = task->tstate;
PyTaskletObject *hold = ts->st.current;
PyTaskletObject **chain = &ts->st.current;
@@ -36,7 +36,7 @@ slp_current_insert_after(PyTaskletObject *task)
void
slp_current_uninsert(PyTaskletObject *task)
{
- PyThreadState *ts = task->cstate->tstate;
+ PyThreadState *ts = task->tstate;
PyTaskletObject *hold = ts->st.current;
PyTaskletObject **chain = &ts->st.current;
@@ -74,7 +74,7 @@ slp_current_unremove(PyTaskletObject* task)
static int
tasklet_has_c_stack(PyTaskletObject *t)
{
- return t->f.frame && t->cstate && t->cstate->nesting_level != 0 ;
+ return t->f.frame && t->cstate;
}
static int
@@ -93,7 +93,6 @@ tasklet_traverse(PyTaskletObject *t, visitproc visit, void *arg)
Py_VISIT(f);
}
Py_VISIT(t->tempval);
- Py_VISIT(t->cstate);
return 0;
}
@@ -117,11 +116,6 @@ tasklet_clear(PyTaskletObject *t)
{
tasklet_clear_frames(t);
TASKLET_SETVAL(t, Py_None); /* always non-zero */
-
- /* unlink task from cstate */
- if (t->cstate != NULL && t->cstate->task == t)
- t->cstate->task = NULL;
- Py_CLEAR(t->cstate);
}
/*
@@ -140,7 +134,7 @@ kill_finally (PyObject *ob)
{
PyThreadState *ts = PyThreadState_GET();
PyTaskletObject *self = (PyTaskletObject *) ob;
- int is_mine = ts == self->cstate->tstate;
+ int is_mine = ts == self->tstate;
/* this could happen if we have a refcount bug, so catch it here.
assert(self != ts->st.current);
@@ -182,34 +176,18 @@ tasklet_dealloc(PyTaskletObject *t)
tasklet_clear_frames(t);
if (t->tsk_weakreflist != NULL)
PyObject_ClearWeakRefs((PyObject *)t);
- if (t->cstate != NULL) {
- assert(t->cstate->task != t || t->cstate->ob_size == 0);
- Py_DECREF(t->cstate);
- }
Py_DECREF(t->tempval);
Py_XDECREF(t->def_globals);
t->ob_type->tp_free((PyObject*)t);
}
-static PyTaskletObject *
-PyTasklet_New_M(PyTypeObject *type, PyObject *func)
-{
- char *fmt = "(O)";
-
- if (func == NULL) fmt = NULL;
- return (PyTaskletObject *) PyStackless_CallMethod_Main(
- (PyObject*)type, NULL, fmt, func);
-}
-
PyTaskletObject *
PyTasklet_New(PyTypeObject *type, PyObject *func)
{
PyThreadState *ts = PyThreadState_GET();
PyTaskletObject *t;
- /* we always need a cstate, so be sure to initialize */
- if (ts->st.initial_stub == NULL) return PyTasklet_New_M(type, func);
if (func != NULL && !PyCallable_Check(func))
TYPE_ERROR("tasklet function must be a callable", NULL);
if (type == NULL) type = &PyTasklet_Type;
@@ -223,19 +201,13 @@ PyTasklet_New(PyTypeObject *type, PyObject *func)
if (func == NULL)
func = Py_None;
Py_INCREF(func);
- t->tempval = func;
+ t->tempval = func;
t->tsk_weakreflist = NULL;
- Py_INCREF(ts->st.initial_stub);
- t->cstate = ts->st.initial_stub;
+ t->cstate = 0;
+ t->tstate = ts;
+ t->nesting_level = 0;
t->def_globals = PyEval_GetGlobals();
Py_XINCREF(t->def_globals);
- if (ts != slp_initial_tstate) {
- /* make sure to kill tasklets with their thread */
- if (slp_ensure_linkage(t)) {
- Py_DECREF(t);
- return NULL;
- }
- }
}
return t;
}
@@ -324,12 +296,11 @@ tasklet_reduce(PyTaskletObject * t)
f = f->f_back;
}
if (PyList_Reverse(lis)) goto err_exit;
- assert(t->cstate != NULL);
tup = Py_BuildValue("(O()(" TASKLET_TUPLEFMT "))",
t->ob_type,
t->flags,
t->tempval,
- t->cstate->nesting_level,
+ t->nesting_level,
lis
);
err_exit:
@@ -516,14 +487,14 @@ static TASKLET_INSERT_HEAD(impl_tasklet_insert)
if (task->next == NULL) {
if (task->f.frame == NULL && task != ts->st.current)
RUNTIME_ERROR("You cannot run an unbound(dead) tasklet", -1);
- if (task->cstate->tstate->st.main == NULL)
+ if (task->tstate->st.main == NULL)
RUNTIME_ERROR("Target thread isn't initialized", -1);
Py_INCREF(task);
slp_current_insert(task);
/* The tasklet may belong to a different thread, and that thread may
* be blocked, waiting for something to do!
*/
- slp_thread_unblock(task->cstate->tstate);
+ slp_thread_unblock(task->tstate);
}
return 0;
}
@@ -584,7 +555,7 @@ static TASKLET_RUN_HEAD(impl_tasklet_run)
if (ts->st.main == NULL) return PyTasklet_Run_M(task);
inserted = task->next == NULL;
- if (ts == task->cstate->tstate) {
+ if (ts == task->tstate) {
/* same thread behaviour. Insert at the end of the queue and then
* switch to that task. Notice that this behaviour upsets FIFO
* order
@@ -592,7 +563,7 @@ static TASKLET_RUN_HEAD(impl_tasklet_run)
fail = impl_tasklet_insert(task);
} else {
/* interthread. */
- PyThreadState *rts = task->cstate->tstate;
+ PyThreadState *rts = task->tstate;
PyTaskletObject *current = rts->st.current;
if (rts->st.thread.is_idle) {
/* remote thread is blocked, or unblocked and hasn't got the GIL yet.
@@ -741,20 +712,13 @@ tasklet_set_ignore_nesting(PyObject *self, PyObject *flag)
static int
bind_tasklet_to_frame(PyTaskletObject *task, PyFrameObject *frame)
{
- PyThreadState *ts = task->cstate->tstate;
-
+ PyThreadState *ts = PyThreadState_GET();
+
if (task->f.frame != NULL)
RUNTIME_ERROR("tasklet is already bound to a frame", -1);
task->f.frame = frame;
- if (task->cstate != ts->st.initial_stub) {
- PyCStackObject *hold = task->cstate;
- task->cstate = ts->st.initial_stub;
- Py_INCREF(task->cstate);
- Py_DECREF(hold);
- if (ts != slp_initial_tstate)
- if (slp_ensure_linkage(task))
- return -1;
- }
+ task->tstate = ts;
+ task->nesting_level = 0;
return 0;
/* note: We expect that f_back is NULL, or will be adjusted immediately */
}
@@ -861,7 +825,7 @@ static TASKLET_THROW_HEAD(impl_tasklet_throw)
* f.frame is null for the running tasklet and a dead tasklet
* A new tasklet has a CFrame
*/
- if (self->f.frame == NULL && self != self->cstate->tstate->st.current) {
+ if (self->f.frame == NULL && self != self->tstate->st.current) {
/* however, allow tasklet exit errors for already dead tasklets */
if (PyObject_IsSubclass(((PyBombObject*)bomb)->curexc_type, PyExc_TaskletExit)) {
Py_DECREF(bomb);
@@ -1176,8 +1140,7 @@ tasklet_get_recursion_depth(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return PyInt_FromLong(ts->st.current == task ? ts->recursion_depth
: task->recursion_depth);
}
@@ -1187,8 +1150,7 @@ PyTasklet_GetRecursionDepth(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return ts->st.current == task ? ts->recursion_depth
: task->recursion_depth;
}
@@ -1199,11 +1161,10 @@ tasklet_get_nesting_level(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return PyInt_FromLong(
ts->st.current == task ? ts->st.nesting_level
- : task->cstate->nesting_level);
+ : task->nesting_level);
}
int
@@ -1211,10 +1172,9 @@ PyTasklet_GetNestingLevel(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return ts->st.current == task ? ts->st.nesting_level
- : task->cstate->nesting_level;
+ : task->nesting_level;
}
@@ -1264,11 +1224,10 @@ tasklet_restorable(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return PyBool_FromLong(
0 == (ts->st.current == task ? ts->st.nesting_level
- : task->cstate->nesting_level) );
+ : task->nesting_level) );
}
int
@@ -1276,10 +1235,9 @@ PyTasklet_Restorable(PyTaskletObject *task)
{
PyThreadState *ts;
- assert(task->cstate != NULL);
- ts = task->cstate->tstate;
+ ts = task->tstate;
return 0 == (ts->st.current == task ? ts->st.nesting_level
- : task->cstate->nesting_level);
+ : task->nesting_level);
}
static PyObject *
@@ -1322,14 +1280,10 @@ tasklet_get_prev(PyTaskletObject *task)
static PyObject *
tasklet_thread_id(PyTaskletObject *task)
{
- return PyInt_FromLong(task->cstate->tstate->thread_id);
+ return PyInt_FromLong(task->tstate->thread_id);
}
static PyMemberDef tasklet_members[] = {
- {"cstate", T_OBJECT, offsetof(PyTaskletObject, cstate), READONLY,
- PyDoc_STR("the C stack object associated with the tasklet.\n\
- Every tasklet has a cstate, even if it is a trivial one.\n\
- Please see the cstate doc and the stackless documentation.")},
{"tempval", T_OBJECT, offsetof(PyTaskletObject, tempval), 0},
/* blocked, slicing_lock, atomic and such are treated by tp_getset */
{0}
diff --git a/Stackless/pickling/prickelpit.c b/Stackless/pickling/prickelpit.c
index ff3f08a42a3a92..878093625c920d 100644
--- a/Stackless/pickling/prickelpit.c
+++ b/Stackless/pickling/prickelpit.c
@@ -7,9 +7,6 @@
#include "pickling/prickelpit.h"
#include "module/channelobject.h"
-/* platform specific constants */
-#include "platf/slp_platformselect.h"
-
/******************************************************
type template and support for pickle helper types
diff --git a/Stackless/pickling/safe_pickle.c b/Stackless/pickling/safe_pickle.c
index 964d2282ff1a31..ff02112c7cbd71 100644
--- a/Stackless/pickling/safe_pickle.c
+++ b/Stackless/pickling/safe_pickle.c
@@ -1,134 +1,59 @@
#include "Python.h"
#ifdef STACKLESS
-#include "compile.h"
-
-#include "core/stackless_impl.h"
-#include "platf/slp_platformselect.h"
+#include "core/stackless_tealet.h"
/* safe pickling */
-static int(*cPickle_save)(PyObject *, PyObject *, int) = NULL;
-
-static PyObject *
-pickle_callback(PyFrameObject *f, int exc, PyObject *retval)
+typedef struct pickle_args
{
- PyThreadState *ts = PyThreadState_GET();
- PyTaskletObject *cur = ts->st.current;
- PyCStackObject *cst;
- PyCFrameObject *cf = (PyCFrameObject *) f;
- intptr_t *saved_base;
-
- /* store and update thread state */
- ts->st.nesting_level = 1; /* always hard-switch from this one now */
- /* swap the cstates, hold on to the return one */
- cst = cur->cstate;
- cur->cstate = ts->st.initial_stub;
- Py_INCREF(cur->cstate);
-
- /* We must base our new stack from here, because oterwise we might find
- * ourselves in an infinite loop of stack spilling.
- */
- saved_base = ts->st.cstack_root;
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &f;
- Py_DECREF(retval);
- cf->i = cPickle_save(cf->ob1, cf->ob2, cf->n);
- ts->st.cstack_root = saved_base;
-
- /* jump back. No decref, frame contains result. */
- Py_DECREF(cur->cstate);
- cur->cstate = cst;
- ts->frame = cf->f_back;
- slp_transfer_return(cst);
- /* never come here */
- return NULL;
+ PyThreadState *ts;
+ tealet_t *return_to;
+ int (*save)(PyObject *, PyObject *, int);
+ PyObject *self;
+ PyObject *args;
+ int pers_save;
+ int result;
+} pickle_args;
+
+
+static tealet_t *
+pickle_callback(tealet_t *current, void *arg)
+{
+ pickle_args *args = (pickle_args*)arg;
+ args->ts->st.nesting_level++; /* always hard-switch from this one now */
+ args->result = (*args->save)(args->self, args->args, args->pers_save);
+ args->ts->st.nesting_level--;
+ return args->return_to;
}
-static int pickle_M(PyObject *self, PyObject *args, int pers_save);
-
int
slp_safe_pickling(int(*save)(PyObject *, PyObject *, int),
PyObject *self, PyObject *args, int pers_save)
{
PyThreadState *ts = PyThreadState_GET();
- PyTaskletObject *cur = ts->st.current;
- int ret = -1;
- PyCFrameObject *cf = NULL;
- PyCStackObject *cst;
-
- if (ts->st.cstack_root == NULL) {
- /* mark the stack spilling base */
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &save;
- ret = (*save)(self, args, pers_save);
- ts->st.cstack_root = NULL;
- return ret;
+ pickle_args *pargs = PyMem_MALLOC(sizeof(pickle_args));
+ void *ppargs;
+ int result;
+ if (!pargs) {
+ PyErr_NoMemory();
+ return -1;
}
-
- cPickle_save = save;
-
- if (ts->st.main == NULL)
- return pickle_M(self, args, pers_save);
-
- cf = slp_cframe_new(pickle_callback, 1);
- if (cf == NULL)
- goto finally;
- Py_INCREF(self);
- cf->ob1 = self;
- Py_INCREF(args);
- cf->ob2 = args;
- cf->n = pers_save;
- ts->frame = (PyFrameObject *) cf;
- cst = cur->cstate;
- cur->cstate = NULL;
- if (slp_transfer(&cur->cstate, NULL, cur) < 0)
- return -1; /* fatal */
- Py_XDECREF(cur->cstate);
- cur->cstate = cst;
- ret = cf->i;
-finally:
- Py_XDECREF(cf);
- return ret;
-}
-
-/* safe unpickling is not needed */
-
-
-/*
- * the following stuff is only needed in the rare case that we are
- * run without any initialisation. In this case, we don't save stack
- * but use slp_eval_frame, which initializes everything.
- */
-
-static PyObject *_self, *_args;
-static int _pers_save;
-
-static PyObject *
-pickle_runmain(PyFrameObject *f, int exc, PyObject *retval)
-{
- PyThreadState *ts = PyThreadState_GET();
- Py_XDECREF(retval);
- ts->frame = f->f_back;
- Py_DECREF(f);
- return PyInt_FromLong(cPickle_save(_self, _args, _pers_save));
+ pargs->ts = ts;
+ pargs->return_to = tealet_current(ts->st.tealet_main);
+ pargs->save = save;
+ pargs->self = self;
+ pargs->args = args;
+ pargs->pers_save = pers_save;
+
+ ppargs = (void*)pargs;
+ result = slp_run_initial_stub(ts, pickle_callback, &ppargs);
+ if (result == 0)
+ result = pargs->result;
+ PyMem_FREE(pargs);
+ return result;
}
-static int
-pickle_M(PyObject *self, PyObject *args, int pers_save)
-{
- PyThreadState *ts = PyThreadState_GET();
- PyCFrameObject *cf = slp_cframe_new(pickle_runmain, 0);
- int ret;
- intptr_t *old_root;
-
- if (cf == NULL) return -1;
- _self = self;
- _args = args;
- _pers_save = pers_save;
- old_root = ts->st.cstack_root;
- ts->st.cstack_root = STACK_REFPLUS + (intptr_t *) &self;
- ret = slp_int_wrapper(slp_eval_frame((PyFrameObject *)cf));
- ts->st.cstack_root = old_root;
- return ret;
-}
-#endif
+/* safe unpickling is not needed */
+#endif
\ No newline at end of file
diff --git a/Stackless/platf/switch_amd64_unix_gas.s b/Stackless/platf/switch_amd64_unix_gas.s
deleted file mode 100644
index 6f61106572728b..00000000000000
--- a/Stackless/platf/switch_amd64_unix_gas.s
+++ /dev/null
@@ -1,56 +0,0 @@
-# NOTE: This is not yet safe to use. Checked in for the same of reference.
-#
-# (int) slp_switch (void);
-
- .text
- .type slp_switch, @function
- # This next line is required to the C code can find and
- # link against this function.
- .global slp_switch
-slp_switch:
- pushq %rbp
- pushq %r15
- pushq %r14
- pushq %r13
- pushq %r12
- pushq %rbx
-
- # Disabled for now, which should ideally give us identical
- # behaviour to the inline C version. Can add this when we
- # are ready for it.
- #subq $8, %rsp
- #stmxcsr (%rsp)
-
- movq %rsp, %rdi
-
- call slp_save_state # diff = slp_save_state([?]stackref)
-
- cmp $-1, %rax # if (diff == -1)
- je .exit # return -1;
-
- cmp $1, %rax # if (diff == 1)
- je .no_restore # return 0;
-
-.restore:
- add %rax, %rsp # Adjust the stack pointer for the state we are restoring.
-
- call slp_restore_state # slp_restore_state()
-
-.no_restore:
- xor %rax, %rax # Switch successful (whether we restored or not).
-
-.exit:
- #ldmxcsr (%rsp)
- #addq $8, %rsp
-
- popq %rbx
- popq %r12
- popq %r13
- popq %r14
- popq %r15
- # rbp gets popped by the leave statement
-
- leave
- ret
-.LFE11:
- .size slp_switch, .-slp_switch
diff --git a/Stackless/tealet/platf_slp/mkswitch_stack.py b/Stackless/tealet/platf_slp/mkswitch_stack.py
new file mode 100644
index 00000000000000..5a047ef7f2998e
--- /dev/null
+++ b/Stackless/tealet/platf_slp/mkswitch_stack.py
@@ -0,0 +1,71 @@
+"""
+ mkswitch_stack.py
+
+ Purpose:
+ Generate an include file from the platform dependant
+ include files mentioned in slp_platformselect.h .
+
+ The existing slp_switch implementations are calling
+ the macros slp_save_state and slp_restore_state.
+ Now I want to support real stack switching, that is,
+ the stack is not modified in place, but we jump to
+ a different stack, without copying anything.
+ This costs a lot of memory and should be used for
+ a few high-speed tasklets, only.
+
+ In order to keep things simple, I'm not special-casing
+ the support macroes, but use a different macro set.
+ The machine code is the same, therefore the implementation
+ can be generated from the existing include files.
+
+ We generate a new include file called slp_switch_stack.h .
+"""
+
+def parse_platformselect():
+ fin_name = "slp_platformselect.h"
+ fin = file(fin_name)
+ fout_name = "slp_switch_stack.h"
+ fout = file(fout_name, "w")
+ import sys
+ print>>fout, "/* this file is generated by mkswitch_stack.py, don't edit */"
+ print>>fout
+ for line in fin:
+ tokens = line.split()
+ if not tokens: continue
+ tok = tokens[0]
+ if tok == "#endif":
+ print>>fout, line
+ break # done
+ if tok in ("#if", "#elif"):
+ print>>fout, line
+ elif tok == "#include":
+ finc_name = tokens[1][1:-1]
+ txt = parse_switch(finc_name)
+ print>>fout, txt
+
+edits = (
+ ("slp_switch", "slp_switch_stack"),
+ ("SLP_SAVE_STATE", "SLP_STACK_BEGIN"),
+ ("SLP_RESTORE_STATE", "SLP_STACK_END"),
+)
+
+def parse_switch(fname):
+ f = file(fname)
+ res = []
+ for line in f:
+ if line.strip() == "static int":
+ res.append(line)
+ break
+ for line in f:
+ res.append(line)
+ if line.rstrip() == "}":
+ break
+ # end of procedure.
+ # now substitute
+ s = "".join(res)
+ for txt, repl in edits:
+ s = s.replace(txt, repl)
+ return s
+
+if __name__ == "__main__":
+ parse_platformselect()
\ No newline at end of file
diff --git a/Stackless/tealet/platf_slp/slp_fallback.h b/Stackless/tealet/platf_slp/slp_fallback.h
new file mode 100644
index 00000000000000..ef4687f3df025b
--- /dev/null
+++ b/Stackless/tealet/platf_slp/slp_fallback.h
@@ -0,0 +1,67 @@
+/* slp_fallback.h
+ * Implements a fallback mechanism where the stacless python switching code be used.
+ * Stackless python switching code uses global variables to communicate and thus is not
+ * thread safe. We do not attempt to provide a lock for those global variables here, it
+ * is up to the user in each case to ensure that switching only occurs on one thread at
+ * a time.
+ */
+ #include
+
+
+/* rename the slp_switch function that the slp headers will define */
+#define slp_switch fallback_slp_switch
+
+/* sttic vars to pass inforamtion around. This is what makes this non-threadsafe */
+typedef void *(*save_restore_t)(void*, void*);
+static save_restore_t fallback_save_state=NULL, fallback_restore_state=NULL;
+static void *fallback_extra=NULL;
+static void *fallback_newstack = NULL;
+
+/* call the provided save_satate method with its arguments.
+ * compute a proper stack delta and store the result for later
+ */
+#define SLP_SAVE_STATE(stackref, stsizediff) do {\
+ intptr_t stsizeb; \
+ void *newstack; \
+ stackref += STACK_MAGIC; \
+ newstack = fallback_save_state(stackref, fallback_extra); \
+ fallback_newstack = newstack; /* store this for restore_state */ \
+ if ((intptr_t)newstack & 1) \
+ return (int)newstack; \
+ /* compute the delta expected by old switching code */ \
+ stsizediff = ((intptr_t*) newstack - (intptr_t*)stackref) * sizeof(intptr_t); \
+} while (0)
+
+/* call the restore_state using the stored data */
+#define SLP_RESTORE_STATE() do { \
+ fallback_restore_state(fallback_newstack, fallback_extra); \
+} while(0)
+
+#define SLP_EVAL
+#include "slp_platformselect.h"
+
+/* This is a wrapper that takes care of setting the appropriate globals */
+#undef slp_switch
+static void *slp_switch(save_restore_t save_state,
+ save_restore_t restore_state,
+ void *extra)
+{
+ /* need to store the restore information on the stack */
+ void *result;
+ fallback_save_state = save_state;
+ fallback_restore_state = restore_state;
+ fallback_extra = extra;
+
+ fallback_slp_switch();
+ result = fallback_newstack;
+
+ /* clearing them again is prudent */
+ fallback_save_state = fallback_restore_state = NULL;
+ fallback_extra = fallback_newstack = NULL;
+
+ /* successful switch was indicated by save_state returning an even result */
+ if (! ((intptr_t)result & 1))
+ result = NULL;
+ /* otherwise it is 1 or -1 */
+ return result;
+}
\ No newline at end of file
diff --git a/Stackless/platf/slp_platformselect.h b/Stackless/tealet/platf_slp/slp_platformselect.h
similarity index 96%
rename from Stackless/platf/slp_platformselect.h
rename to Stackless/tealet/platf_slp/slp_platformselect.h
index 59ad0ef371404e..90b46370c6e463 100644
--- a/Stackless/platf/slp_platformselect.h
+++ b/Stackless/tealet/platf_slp/slp_platformselect.h
@@ -2,9 +2,9 @@
* Platform Selection for Stackless Python
*/
-#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86)
+#if defined(_M_IX86)
#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */
-#elif defined(MS_WIN64) && defined(_M_X64)
+#elif defined(_M_X64)
#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */
#elif defined(__GNUC__) && defined(__i386__)
#include "switch_x86_unix.h" /* gcc on X86 */
@@ -28,6 +28,8 @@
#include "switch_mips_unix.h" /* MIPS */
#elif defined(SN_TARGET_PS3)
#include "switch_ps3_SNTools.h" /* Sony PS3 */
+#else
+#error "platform unsupported by stackless python"
#endif
/* default definitions if not defined in above files */
diff --git a/Stackless/tealet/platf_slp/slp_switch_stack.h b/Stackless/tealet/platf_slp/slp_switch_stack.h
new file mode 100644
index 00000000000000..4eb03f379a6f2f
--- /dev/null
+++ b/Stackless/tealet/platf_slp/slp_switch_stack.h
@@ -0,0 +1,279 @@
+/* this file is generated by mkswitch_stack.py, don't edit */
+
+#if defined(MS_WIN32) && !defined(MS_WIN64) && defined(_M_IX86)
+
+static int
+slp_switch_stack(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_STACK_BEGIN(stackref, stsizediff);
+ __asm {
+ mov eax, stsizediff
+ add esp, eax
+ add ebp, eax
+ }
+ SLP_STACK_END();
+ return 0;
+ }
+#pragma warning(default:4731)
+}
+
+#elif defined(MS_WIN64) && defined(_M_X64)
+
+
+#elif defined(__GNUC__) && defined(__i386__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+#if STACKLESS_FRHACK
+ __asm__ volatile ("" : : : "esi", "edi");
+#else
+ __asm__ volatile ("" : : : "ebx", "esi", "edi");
+#endif
+ __asm__ ("movl %%esp, %0" : "=g" (stackref));
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "addl %0, %%esp\n"
+ "addl %0, %%ebp\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+#if STACKLESS_FRHACK
+ __asm__ volatile ("" : : : "esi", "edi");
+#else
+ __asm__ volatile ("" : : : "ebx", "esi", "edi");
+#endif
+}
+
+#elif defined(__GNUC__) && defined(__amd64__)
+
+static int
+slp_switch_stack(void)
+{
+ register long *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("movq %%rsp, %0" : "=g" (stackref));
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "addq %0, %%rsp\n"
+ "addq %0, %%rbp\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+}
+
+#elif defined(__GNUC__) && defined(__PPC__) && defined(__linux__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("mr %0, 1" : "=g" (stackref) : );
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "mr 11, %0\n"
+ "add 1, 1, 11\n"
+ "add 30, 30, 11\n"
+ : /* no outputs */
+ : "g" (stsizediff)
+ : "11"
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+}
+
+#elif defined(__GNUC__) && defined(__ppc__) && defined(__APPLE__)
+
+static int
+slp_switch_stack(void)
+{
+ static int x = 0;
+ register intptr_t *stackref;
+ register int stsizediff;
+ __asm__ volatile (
+ "; asm block 1\n"
+ : /* no outputs */
+ : "r" (x)
+ : REGS_TO_SAVE
+ );
+ __asm__ ("; asm block 2\n\tmr %0, r1" : "=g" (stackref) : );
+ {
+ SLP_STACK_BEGIN(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_STACK_END();
+ return 0;
+ }
+}
+
+#elif defined(__GNUC__) && defined(sparc) && defined(sun)
+
+static int
+slp_switch_stack(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_STACK_BEGIN into a local block */
+
+ SLP_STACK_BEGIN(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_STACK_END
+ 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_STACK_END();
+
+ /* 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;
+ }
+}
+
+#elif defined(__GNUC__) && defined(__s390__) && defined(__linux__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("lr %0, 15" : "=g" (stackref) : );
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "ar 15, %0"
+ : /* no outputs */
+ : "g" (stsizediff)
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+}
+
+#elif defined(__GNUC__) && defined(__s390x__) && defined(__linux__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("lr %0, 15" : "=g" (stackref) : );
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "ar 15, %0"
+ : /* no outputs */
+ : "g" (stsizediff)
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+}
+
+#elif defined(__GNUC__) && defined(__arm__) && defined(__thumb__)
+
+
+#elif defined(__GNUC__) && defined(__arm32__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+ __asm__ ("mov %0,sp" : "=g" (stackref));
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ volatile (
+ "add sp,sp,%0\n"
+ "add fp,fp,%0\n"
+ :
+ : "r" (stsizediff)
+ );
+ SLP_STACK_END();
+ return 0;
+ }
+ __asm__ volatile ("" : : : REGS_TO_SAVE);
+}
+
+#elif defined(__GNUC__) && defined(__mips__) && defined(__linux__)
+
+static int
+slp_switch_stack(void)
+{
+ register int *stackref, stsizediff;
+ /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */
+ __asm__ ("move %0, $29" : "=r" (stackref) : );
+ {
+ SLP_STACK_BEGIN(stackref, stsizediff);
+ __asm__ __volatile__ (
+#ifdef __mips64
+ "daddu $29, %0\n"
+#else
+ "addu $29, %0\n"
+#endif
+ : /* no outputs */
+ : "r" (stsizediff)
+ );
+ SLP_STACK_END();
+ }
+ /* __asm__ __volatile__ ("" : : : REGS_TO_SAVE); */
+ return 0;
+}
+
+#endif
+
diff --git a/Stackless/platf/switch_amd64_unix.h b/Stackless/tealet/platf_slp/switch_amd64_unix.h
similarity index 100%
rename from Stackless/platf/switch_amd64_unix.h
rename to Stackless/tealet/platf_slp/switch_amd64_unix.h
diff --git a/Stackless/platf/switch_arm32_gcc.h b/Stackless/tealet/platf_slp/switch_arm32_gcc.h
similarity index 100%
rename from Stackless/platf/switch_arm32_gcc.h
rename to Stackless/tealet/platf_slp/switch_arm32_gcc.h
diff --git a/Stackless/platf/switch_arm_thumb_gas.s b/Stackless/tealet/platf_slp/switch_arm_thumb_gas.s
similarity index 100%
rename from Stackless/platf/switch_arm_thumb_gas.s
rename to Stackless/tealet/platf_slp/switch_arm_thumb_gas.s
diff --git a/Stackless/platf/switch_arm_thumb_gcc.h b/Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h
similarity index 100%
rename from Stackless/platf/switch_arm_thumb_gcc.h
rename to Stackless/tealet/platf_slp/switch_arm_thumb_gcc.h
diff --git a/Stackless/platf/switch_mips_unix.h b/Stackless/tealet/platf_slp/switch_mips_unix.h
similarity index 100%
rename from Stackless/platf/switch_mips_unix.h
rename to Stackless/tealet/platf_slp/switch_mips_unix.h
diff --git a/Stackless/platf/switch_ppc_macosx.h b/Stackless/tealet/platf_slp/switch_ppc_macosx.h
similarity index 100%
rename from Stackless/platf/switch_ppc_macosx.h
rename to Stackless/tealet/platf_slp/switch_ppc_macosx.h
diff --git a/Stackless/platf/switch_ppc_unix.h b/Stackless/tealet/platf_slp/switch_ppc_unix.h
similarity index 99%
rename from Stackless/platf/switch_ppc_unix.h
rename to Stackless/tealet/platf_slp/switch_ppc_unix.h
index ccc893b2c2022b..f0bf07845f1dcf 100644
--- a/Stackless/platf/switch_ppc_unix.h
+++ b/Stackless/tealet/platf_slp/switch_ppc_unix.h
@@ -65,7 +65,7 @@ slp_switch(void)
* 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
diff --git a/Stackless/platf/switch_ps3_SNTools.h b/Stackless/tealet/platf_slp/switch_ps3_SNTools.h
similarity index 100%
rename from Stackless/platf/switch_ps3_SNTools.h
rename to Stackless/tealet/platf_slp/switch_ps3_SNTools.h
diff --git a/Stackless/platf/switch_ps3_SNTools.s b/Stackless/tealet/platf_slp/switch_ps3_SNTools.s
similarity index 100%
rename from Stackless/platf/switch_ps3_SNTools.s
rename to Stackless/tealet/platf_slp/switch_ps3_SNTools.s
diff --git a/Stackless/platf/switch_s390_unix.h b/Stackless/tealet/platf_slp/switch_s390_unix.h
similarity index 100%
rename from Stackless/platf/switch_s390_unix.h
rename to Stackless/tealet/platf_slp/switch_s390_unix.h
diff --git a/Stackless/platf/switch_sparc_sun_gcc.h b/Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h
similarity index 100%
rename from Stackless/platf/switch_sparc_sun_gcc.h
rename to Stackless/tealet/platf_slp/switch_sparc_sun_gcc.h
diff --git a/Stackless/platf/switch_x64_masm.asm b/Stackless/tealet/platf_slp/switch_x64_masm.asm
similarity index 100%
rename from Stackless/platf/switch_x64_masm.asm
rename to Stackless/tealet/platf_slp/switch_x64_masm.asm
diff --git a/Stackless/platf/switch_x64_msvc.h b/Stackless/tealet/platf_slp/switch_x64_msvc.h
similarity index 100%
rename from Stackless/platf/switch_x64_msvc.h
rename to Stackless/tealet/platf_slp/switch_x64_msvc.h
diff --git a/Stackless/platf/switch_x86_msvc.h b/Stackless/tealet/platf_slp/switch_x86_msvc.h
similarity index 100%
rename from Stackless/platf/switch_x86_msvc.h
rename to Stackless/tealet/platf_slp/switch_x86_msvc.h
diff --git a/Stackless/platf/switch_x86_unix.h b/Stackless/tealet/platf_slp/switch_x86_unix.h
similarity index 93%
rename from Stackless/platf/switch_x86_unix.h
rename to Stackless/tealet/platf_slp/switch_x86_unix.h
index 6668e659bb5ff3..c95362c0acbea5 100644
--- a/Stackless/platf/switch_x86_unix.h
+++ b/Stackless/tealet/platf_slp/switch_x86_unix.h
@@ -34,7 +34,7 @@ slp_switch(void)
#if STACKLESS_FRHACK
__asm__ volatile ("" : : : "esi", "edi");
#else
- __asm__ volatile ("" : : : "ebx", "esi", "edi");
+ __asm__ volatile ("" : : : "ebx", "esi", "edi", "ebp");
#endif
__asm__ ("movl %%esp, %0" : "=g" (stackref));
{
@@ -51,7 +51,7 @@ slp_switch(void)
#if STACKLESS_FRHACK
__asm__ volatile ("" : : : "esi", "edi");
#else
- __asm__ volatile ("" : : : "ebx", "esi", "edi");
+ __asm__ volatile ("" : : : "ebx", "esi", "edi", "ebp");
#endif
}
diff --git a/Stackless/tealet/slp_platformselect.h b/Stackless/tealet/slp_platformselect.h
new file mode 100644
index 00000000000000..e86580628f9e7d
--- /dev/null
+++ b/Stackless/tealet/slp_platformselect.h
@@ -0,0 +1,32 @@
+
+/* define USE_SLP_FALLBACK here if you want to test the SLP_FALLBACK mechanism */
+#define ENABLE_SLP_FALLBACK
+/* #define USE_SLP_FALLBACK */
+
+#if defined USE_SLP_FALLBACK && !defined ENABLE_SLP_FALLBACK
+#define ENABLE_SLP_FALLBACK
+#endif
+
+#ifndef USE_SLP_FALLBACK
+#if defined(_M_IX86)
+#include "switch_x86_msvc.h" /* MS Visual Studio on X86 */
+#elif defined(_M_X64)
+#include "switch_x64_msvc.h" /* MS Visual Studio on X64 */
+#elif defined(__GNUC__) && defined(__amd64__)
+#include "switch_x86_64_gcc.h" /* gcc on amd64 */
+#elif defined(__GNUC__) && defined(__i386__)
+#include "switch_x86_gcc.h" /* gcc on X86 */
+#else
+#ifdef ENABLE_SLP_FALLBACK
+#define USE_SLP_FALLBACK
+#else
+#error "Unsupported platform!"
+#endif
+#endif
+#endif
+
+#ifdef USE_SLP_FALLBACK
+/* hope this is standard C */
+#pragma message("fallback to stackless platform support. Switching is not thread-safe")
+#include "platf_slp/slp_fallback.h"
+#endif
diff --git a/Stackless/tealet/switch_x64_msvc.asm b/Stackless/tealet/switch_x64_msvc.asm
new file mode 100644
index 00000000000000..a9ebd40409703e
--- /dev/null
+++ b/Stackless/tealet/switch_x64_msvc.asm
@@ -0,0 +1,103 @@
+;
+; stack switching code for MASM on x64
+; Kristjan Valur Jonsson, apr 2011
+;
+
+include macamd64.inc
+
+pop_reg MACRO reg
+ pop reg
+ENDM
+
+load_xmm128 macro Reg, Offset
+ movdqa Reg, Offset[rsp]
+endm
+
+.code
+
+;arguments save_state, restore_state, extra are passed in rcx, rdx, r8 respectively
+;slp_switch PROC FRAME
+NESTED_ENTRY slp_switch, _TEXT$00
+ ; save all registers that the x64 ABI specifies as non-volatile.
+ ; This includes some mmx registers. May not always be necessary,
+ ; unless our application is doing 3D, but better safe than sorry.
+ alloc_stack 168; 10 * 16 bytes, plus 8 bytes to make stack 16 byte aligned
+ save_xmm128 xmm15, 144
+ save_xmm128 xmm14, 128
+ save_xmm128 xmm13, 112
+ save_xmm128 xmm12, 96
+ save_xmm128 xmm11, 80
+ save_xmm128 xmm10, 64
+ save_xmm128 xmm9, 48
+ save_xmm128 xmm8, 32
+ save_xmm128 xmm7, 16
+ save_xmm128 xmm6, 0
+
+ push_reg r15
+ push_reg r14
+ push_reg r13
+ push_reg r12
+
+ push_reg rbp
+ push_reg rbx
+ push_reg rdi
+ push_reg rsi
+
+ sub rsp, 20h ;allocate shadow stack space for the arguments (must be multiple of 16)
+ .allocstack 20h
+.endprolog
+
+ ;save argments in nonvolatile registers
+ mov r12, rcx ;save_state
+ mov r13, rdx
+ mov r14, r8
+
+ ; load stack base that we are saving minus the callee argument
+ ; shadow stack. We don't want that clobbered
+ lea rcx, [rsp+20h]
+ mov rdx, r14
+ call r12 ;pass stackpointer, return new stack pointer in eax
+
+ ; an odd value means that we don't restore, could be
+ ; an error (e.g. -1) or indication that we only want
+ ; to save state (1). return that value to the caller.
+ test rax, 1
+ jnz exit
+
+ ;actual stack switch (and re-allocating the shadow stack):
+ lea rsp, [rax-20h]
+
+ mov rcx, rax ;pass new stack pointer
+ mov rdx, r14
+ call r13
+ ;return the rax
+EXIT:
+
+ add rsp, 20h
+ pop_reg rsi
+ pop_reg rdi
+ pop_reg rbx
+ pop_reg rbp
+
+ pop_reg r12
+ pop_reg r13
+ pop_reg r14
+ pop_reg r15
+
+ load_xmm128 xmm15, 144
+ load_xmm128 xmm14, 128
+ load_xmm128 xmm13, 112
+ load_xmm128 xmm12, 96
+ load_xmm128 xmm11, 80
+ load_xmm128 xmm10, 64
+ load_xmm128 xmm9, 48
+ load_xmm128 xmm8, 32
+ load_xmm128 xmm7, 16
+ load_xmm128 xmm6, 0
+ add rsp, 168
+ ret
+
+NESTED_END slp_switch, _TEXT$00
+;slp_switch ENDP
+
+END
\ No newline at end of file
diff --git a/Stackless/tealet/switch_x64_msvc.h b/Stackless/tealet/switch_x64_msvc.h
new file mode 100644
index 00000000000000..1c0617121956b8
--- /dev/null
+++ b/Stackless/tealet/switch_x64_msvc.h
@@ -0,0 +1,7 @@
+/* The actual stack saving function, which just stores the stack,
+ * this declared in an .asm file
+ */
+extern void *slp_switch(void *(*save_state)(void*, void*),
+ void *(*restore_state)(void*, void*),
+ void *extra);
+
diff --git a/Stackless/tealet/switch_x86_64_gcc.h b/Stackless/tealet/switch_x86_64_gcc.h
new file mode 100644
index 00000000000000..c240c1c261ee9b
--- /dev/null
+++ b/Stackless/tealet/switch_x86_64_gcc.h
@@ -0,0 +1,66 @@
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+ void *(*restore_state)(void*, void*),
+ void *extra)
+{
+ void *result;
+ __asm__ volatile (
+ "pushq %%rbp\n"
+ "pushq %%rbx\n" /* push the registers that may contain */
+ "pushq %%rsi\n" /* some value that is meant to be saved */
+ "pushq %%rdi\n"
+ "pushq %%rcx\n"
+ "pushq %%rdx\n"
+ "pushq %%r8\n"
+ "pushq %%r9\n"
+ "pushq %%r10\n"
+ "pushq %%r11\n"
+ "pushq %%r12\n"
+ "pushq %%r13\n"
+ "pushq %%r14\n"
+ "pushq %%r15\n"
+
+ "movq %%rax, %%r12\n" /* save 'restore_state' for later */
+ "movq %%rsi, %%r13\n" /* save 'extra' for later */
+
+ /* arg 2: extra */
+ "movq %%rsp, %%rdi\n" /* arg 1: current (old) stack pointer */
+ "call *%%rcx\n" /* call save_state() */
+
+ "testb $1, %%al\n" /* skip the rest if the return value is odd */
+ "jnz 0f\n"
+
+ "movq %%rax, %%rsp\n" /* change the stack pointer */
+
+ /* From now on, the stack pointer is modified, but the content of the
+ stack is not restored yet. It contains only garbage here. */
+
+ "movq %%r13, %%rsi\n" /* arg 2: extra */
+ "movq %%rax, %%rdi\n" /* arg 1: current (new) stack pointer */
+ "call *%%r12\n" /* call restore_state() */
+
+ /* The stack's content is now restored. */
+
+ "0:\n"
+ "popq %%r15\n"
+ "popq %%r14\n"
+ "popq %%r13\n"
+ "popq %%r12\n"
+ "popq %%r11\n"
+ "popq %%r10\n"
+ "popq %%r9\n"
+ "popq %%r8\n"
+ "popq %%rdx\n"
+ "popq %%rcx\n"
+ "popq %%rdi\n"
+ "popq %%rsi\n"
+ "popq %%rbx\n"
+ "popq %%rbp\n"
+
+ : "=a"(result) /* output variables */
+ : "a"(restore_state), /* input variables */
+ "c"(save_state),
+ "S"(extra)
+ );
+ return result;
+}
diff --git a/Stackless/tealet/switch_x86_gcc.h b/Stackless/tealet/switch_x86_gcc.h
new file mode 100644
index 00000000000000..8621dcc584922f
--- /dev/null
+++ b/Stackless/tealet/switch_x86_gcc.h
@@ -0,0 +1,53 @@
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+ void *(*restore_state)(void*, void*),
+ void *extra)
+{
+ void *result;
+ __asm__ volatile (
+ "pushl %%ebp\n"
+ "pushl %%ebx\n" /* push the registers that may contain */
+ "pushl %%esi\n" /* some value that is meant to be saved */
+ "pushl %%edi\n"
+ "pushl %%ecx\n"
+ "pushl %%edx\n"
+
+ "movl %%eax, %%esi\n" /* save 'restore_state' for later */
+ "movl %%edx, %%edi\n" /* save 'extra' for later */
+
+ "movl %%esp, %%eax\n"
+
+ "pushl %%edx\n" /* arg 2: extra */
+ "pushl %%eax\n" /* arg 1: current (old) stack pointer */
+ "call *%%ecx\n" /* call save_state() */
+
+ "testl $1, %%eax\n" /* skip the rest if the return value is odd */
+ "jnz 0f\n"
+
+ "movl %%eax, %%esp\n" /* change the stack pointer */
+
+ /* From now on, the stack pointer is modified, but the content of the
+ stack is not restored yet. It contains only garbage here. */
+
+ "pushl %%edi\n" /* arg 2: extra */
+ "pushl %%eax\n" /* arg 1: current (new) stack pointer */
+ "call *%%esi\n" /* call restore_state() */
+
+ /* The stack's content is now restored. */
+
+ "0:\n"
+ "addl $8, %%esp\n"
+ "popl %%edx\n"
+ "popl %%ecx\n"
+ "popl %%edi\n"
+ "popl %%esi\n"
+ "popl %%ebx\n"
+ "popl %%ebp\n"
+
+ : "=a"(result) /* output variables */
+ : "a"(restore_state), /* input variables */
+ "c"(save_state),
+ "d"(extra)
+ );
+ return result;
+}
diff --git a/Stackless/tealet/switch_x86_msvc.asm b/Stackless/tealet/switch_x86_msvc.asm
new file mode 100644
index 00000000000000..628643e3975aec
--- /dev/null
+++ b/Stackless/tealet/switch_x86_msvc.asm
@@ -0,0 +1,44 @@
+
+.386
+.model flat, c
+
+.code
+
+slp_switch_raw PROC save_state:DWORD, restore_state:DWORD, extra:DWORD
+
+ ;save registers. EAX ECX and EDX are available for function use and thus
+ ;do not have to be stored.
+ push ebx
+ push esi
+ push edi
+ push ebp
+
+ mov esi, restore_state ; /* save 'restore_state' for later */
+ mov edi, extra ; /* save 'extra' for later */
+
+ mov eax, esp
+
+ push edi ; /* arg 2: extra */
+ push eax ; /* arg 1: current (old) stack pointer */
+ mov ecx, save_state
+ call ecx ; /* call save_state() */
+
+ test eax, 1; /* skip the restore if the return value is odd */
+ jnz exit
+
+ mov esp, eax; /* change the stack pointer */
+
+ push edi ; /* arg 2: extra */
+ push eax ; /* arg 1: current (new) stack pointer */
+ call esi ; /* call restore_state() */
+
+exit:
+ add esp, 8
+ pop ebp
+ pop edi
+ pop esi
+ pop ebx
+ ret
+slp_switch_raw ENDP
+
+end
\ No newline at end of file
diff --git a/Stackless/tealet/switch_x86_msvc.h b/Stackless/tealet/switch_x86_msvc.h
new file mode 100644
index 00000000000000..bd219271f8e281
--- /dev/null
+++ b/Stackless/tealet/switch_x86_msvc.h
@@ -0,0 +1,26 @@
+/* The actual stack saving function, which just stores the stack,
+ * this declared in an .asm file
+ */
+extern void *slp_switch_raw(void *(*save_state)(void*, void*),
+ void *(*restore_state)(void*, void*),
+ void *extra);
+
+#define WIN32_LEAN_AND_MEAN
+#include
+
+/* Store any other runtime information on the local stack */
+#pragma optimize("", off) /* so that autos are stored on the stack */
+#pragma warning(disable:4733) /* disable warning about modifying FS[0] */
+
+static void *slp_switch(void *(*save_state)(void*, void*),
+ void *(*restore_state)(void*, void*),
+ void *extra)
+{
+ /* store the structured exception state for this stack */
+ DWORD seh_state = __readfsdword(FIELD_OFFSET(NT_TIB, ExceptionList));
+ void * result = slp_switch_raw(save_state, restore_state, extra);
+ __writefsdword(FIELD_OFFSET(NT_TIB, ExceptionList), seh_state);
+ return result;
+}
+#pragma warning(default:4733) /* disable warning about modifying FS[0] */
+#pragma optimize("", on)
diff --git a/Stackless/tealet/tealet.c b/Stackless/tealet/tealet.c
new file mode 100644
index 00000000000000..9744fa65639d47
--- /dev/null
+++ b/Stackless/tealet/tealet.c
@@ -0,0 +1,785 @@
+/********** A minimal coroutine package for C **********
+ * By Armin Rigo
+ * Documentation: see the source code of the greenlet package from
+ *
+ * http://codespeak.net/svn/greenlet/trunk/c/_greenlet.c
+ */
+
+#include "tealet.h"
+
+#include
+#include
+#include
+
+/************************************************************
+ * platform specific code
+ */
+
+/* The default stack direction is downwards, 0, but platforms
+ * can redefine it to upwards growing, 1.
+ * Since we support both architectures with descending and
+ * ascending stacks, we use the terms "near" and "far"
+ * to describe stack boundaries. In a typical architecture
+ * with descending stacks, "near" corresponds to a low
+ * address and "far" to a high address.
+ */
+#define STACK_DIRECTION 0
+
+#include "slp_platformselect.h"
+
+#if STACK_DIRECTION == 0
+#define STACK_FAR_MAIN ((char*) -1) /* for stack_far */
+#define STACK_LE(a, b) ((a) <= (b)) /* to compare stack position */
+#define STACK_SUB(a, b) ((a) - (b)) /* to subtract stack pointers */
+#else
+#define STACK_FAR_MAIN ((char*) 1) /* for stack_far */
+#define STACK_LE(a, b) ((b) <= (a)) /* to compare stack position */
+#define STACK_SUB(a, b) ((b) - (a)) /* to subtract stack pointers */
+#endif
+
+/************************************************************/
+
+/* #define DEBUG_DUMP */
+
+#ifdef DEBUG_DUMP
+#include
+#endif
+
+/************************************************************
+ * Structures for maintaining copies of the C stack.
+ */
+
+/* a chunk represents a single segment of saved stack */
+typedef struct tealet_chunk_t
+{
+ struct tealet_chunk_t *next; /* additional chunks */
+ char *stack_near; /* near stack address */
+ size_t size; /* amount of data saved */
+ char data[1]; /* the data follows here */
+} tealet_chunk_t;
+
+/* The main stack structure, contains the initial chunk and a link to further
+ * segments. Stacks can be shared by different tealets, hence the reference
+ * count. They can also be linked into a list of partiallty unsaved
+ * stacks, that are saved only on demand.
+ */
+typedef struct tealet_stack_t
+{
+ int refcount; /* controls lifetime */
+ struct tealet_stack_t **prev; /* previous 'next' pointer */
+ struct tealet_stack_t *next; /* next unsaved stack */
+ char *stack_far; /* the far boundary of this stack */
+ size_t saved; /* total amount of memory saved in all chunks */
+ struct tealet_chunk_t chunk; /* the initial chunk */
+} tealet_stack_t;
+
+
+/* the actual tealet structure as used internally
+ * The main tealet will have stack_far set to the end of memory.
+ * "stack" is zero for a running tealet, otherwise it points
+ * to the saved stack, or is -1 if the sate is invalid.
+ * In addition, stack_far is set to NULL value to indicate
+ * that a tealet is exiting.
+ */
+typedef struct tealet_sub_t {
+ tealet_t base; /* the public part of the tealet */
+ char *stack_far; /* the "far" end of the stack or NULL when exiting */
+ tealet_stack_t *stack; /* saved stack or 0 if active or -1 if invalid*/
+#ifndef debug
+ int id; /* number of this tealet */
+#endif
+} tealet_sub_t;
+
+/* a structure incorporating extra data */
+typedef struct tealet_nonmain_t {
+ tealet_sub_t base;
+ double _extra[1]; /* start of any extra data */
+} tealet_nonmain_t;
+
+/* The main tealet has additional fields for housekeeping */
+typedef struct tealet_main_t {
+ tealet_sub_t base;
+ void *g_user; /* user data pointer for main */
+ tealet_sub_t *g_current;
+ tealet_sub_t *g_target; /* Temporary store when switching */
+ void *g_arg; /* argument passed around when switching */
+ tealet_alloc_t g_alloc; /* the allocation context used */
+ tealet_stack_t *g_prev; /* previously active unsaved stacks */
+#ifndef NDEBUG
+ int g_tealets; /* number of active tealets excluding main */
+ int g_counter; /* total number of tealets */
+#endif
+ double _extra[1]; /* start of any extra data */
+} tealet_main_t;
+
+#define TEALET_IS_MAIN_STACK(t) (((tealet_sub_t *)(t))->stack_far == STACK_FAR_MAIN)
+#define TEALET_GET_MAIN(t) ((tealet_main_t *)(((tealet_t *)(t))->main))
+
+/************************************************************/
+
+int (*_tealet_switchstack)(tealet_main_t*);
+int (*_tealet_initialstub)(tealet_main_t*, tealet_run_t run, void*);
+
+/************************************************************
+ * helpers to call the malloc functions provided by the user
+ */
+static void *tealet_int_malloc(tealet_main_t *main, size_t size)
+{
+ return main->g_alloc.malloc_p(size, main->g_alloc.context);
+}
+static void tealet_int_free(tealet_main_t *main, void *ptr)
+{
+ main->g_alloc.free_p(ptr, main->g_alloc.context);
+}
+
+/*************************************************************
+ * actual stack management routines. Copying, growing
+ * restoring, duplicating, deleting
+ */
+static tealet_stack_t *tealet_stack_new(tealet_main_t *main,
+ char *stack_near, char *stack_far, size_t size)
+{
+ size_t tsize;
+ tealet_stack_t *s;
+
+ tsize = offsetof(tealet_stack_t, chunk.data[0]) + size;
+ s = (tealet_stack_t*)tealet_int_malloc(main, tsize);
+ if (!s)
+ return NULL;
+ s->refcount = 1;
+ s->prev = NULL;
+ s->stack_far = stack_far;
+ s->saved = size;
+
+ s->chunk.next = NULL;
+ s->chunk.stack_near = stack_near;
+ s->chunk.size = size;
+#if STACK_DIRECTION == 0
+ memcpy(&s->chunk.data[0], stack_near, size);
+#else
+ memcpy(&s->chunk.data[0], stack_near-size, size);
+#endif
+ return s;
+}
+
+static int tealet_stack_grow(tealet_main_t *main,
+ tealet_stack_t *stack, size_t size)
+{
+ tealet_chunk_t *chunk;
+ size_t tsize, diff;
+ assert(size > stack->saved);
+
+ diff = size - stack->saved;
+ tsize = offsetof(tealet_chunk_t, data[0]) + diff;
+ chunk = (tealet_chunk_t*)tealet_int_malloc(main, tsize);
+ if (!chunk)
+ return -1;
+#if STACK_DIRECTION == 0
+ chunk->stack_near = stack->chunk.stack_near + stack->saved;
+ memcpy(&chunk->data[0], chunk->stack_near, diff);
+#else
+ chunk->stack_near = stack->chunk.stack_near - stack->saved;
+ memcpy(&chunk->data[0], chunk->stack_near - diff, diff);
+#endif
+ chunk->size = diff;
+ chunk->next = stack->chunk.next;
+ stack->chunk.next = chunk;
+ stack->saved = size;
+ return 0;
+}
+
+static void tealet_stack_restore(tealet_stack_t *stack)
+{
+ tealet_chunk_t *chunk = &stack->chunk;
+ do {
+#if STACK_DIRECTION == 0
+ memcpy(chunk->stack_near, &chunk->data[0], chunk->size);
+#else
+ memcpy(chunk->stack_near - chunk->size, &chunk->data[0], chunk->size);
+#endif
+ chunk = chunk->next;
+ } while(chunk);
+}
+
+static tealet_stack_t *tealet_stack_dup(tealet_stack_t *stack)
+{
+ stack->refcount += 1;
+ return stack;
+}
+
+static void tealet_stack_link(tealet_stack_t *stack, tealet_stack_t **head)
+{
+ assert(stack->prev == NULL);
+ assert(*head != stack);
+ if (*head)
+ assert((*head)->prev == head);
+ stack->next = *head;
+ if (stack->next)
+ stack->next->prev = &stack->next;
+ stack->prev = head;
+ *head = stack;
+}
+
+static void tealet_stack_unlink(tealet_stack_t *stack)
+{
+ tealet_stack_t *next = stack->next;
+ assert(stack->prev);
+ assert(*stack->prev == stack);
+ if (next)
+ assert(next->prev == &stack->next);
+
+ if (next)
+ next->prev = stack->prev;
+ *stack->prev = next;
+ stack->prev = NULL;
+}
+
+static void tealet_stack_decref(tealet_main_t *main, tealet_stack_t *stack)
+{
+ tealet_chunk_t *chunk;
+ if (stack == NULL || --stack->refcount > 0)
+ return;
+ if (stack->prev)
+ tealet_stack_unlink(stack);
+
+ chunk = stack->chunk.next;
+ tealet_int_free(main, (void*)stack);
+ while(chunk) {
+ tealet_chunk_t *next = chunk->next;
+ tealet_int_free(main, (void*)chunk);
+ chunk = next;
+ }
+}
+
+static void tealet_stack_defunct(tealet_main_t *main, tealet_stack_t *stack)
+{
+ /* stack couldn't be grown. Release any extra chunks and mark stack as defunct */
+ tealet_chunk_t *chunk;
+ chunk = stack->chunk.next;
+ stack->chunk.next = NULL;
+ stack->saved = (size_t)-1;
+ while(chunk) {
+ tealet_chunk_t *next = chunk->next;
+ tealet_int_free(main, (void*)chunk);
+ chunk = next;
+ }
+}
+
+
+/***************************************************************
+ * utility functions for allocating and growing stacks
+ */
+
+/* save a new stack, at least up to "saveto" */
+static tealet_stack_t *tealet_stack_saveto(tealet_main_t *main,
+ char *stack_near, char *stack_far, char *saveto, int *full)
+{
+ ptrdiff_t size;
+ if (STACK_LE(stack_far, saveto)) {
+ saveto = stack_far;
+ *full = 1;
+ } else
+ *full = 0;
+ assert(saveto != STACK_FAR_MAIN); /* can't save all of memory */
+ size = STACK_SUB(saveto, stack_near);
+ if (size < 0)
+ size = 0;
+ return tealet_stack_new(main, (char*) stack_near, stack_far, size);
+}
+
+static int tealet_stack_growto(tealet_main_t *main, tealet_stack_t *stack, char* saveto,
+ int *full, int fail_ok)
+{
+ /* Save more of g's stack into the heap -- at least up to 'saveto'
+
+ g->stack_stop |________|
+ | |
+ | __ stop .........
+ | | ==> : :
+ |________| :_______:
+ | | | |
+ | | | |
+ g->stack_start | | |_______| g->stack_copy
+
+ */
+ ptrdiff_t size, saved=(ptrdiff_t)stack->saved;
+ int fail;
+
+ /* We shouldn't be completely saved already */
+ if (stack->stack_far != STACK_FAR_MAIN)
+ assert(STACK_SUB(stack->stack_far, stack->chunk.stack_near) > saved);
+
+ /* truncate the "stop" */
+ if (STACK_LE(stack->stack_far, saveto)) {
+ saveto = stack->stack_far;
+ *full = 1;
+ } else
+ *full = 0;
+
+ /* total saved size expected after this */
+ assert(saveto != STACK_FAR_MAIN); /* can't save them all */
+ size = STACK_SUB(saveto, stack->chunk.stack_near);
+ if (size <= saved)
+ return 0; /* nothing to do */
+
+ fail = tealet_stack_grow(main, stack, size);
+ if (fail == 0)
+ return 0;
+
+ if (fail_ok)
+ return fail; /* caller can deal with failures */
+ /* we cannot fail. Mark this stack as defunct and continue */
+ tealet_stack_defunct(main, stack);
+ *full = 1;
+ return 0;
+}
+
+/* Grow a list og stacks to a certain limit. Unlink those that
+ * become fully saved.
+ */
+static int tealet_stack_grow_list(tealet_main_t *main, tealet_stack_t *list,
+ char *saveto, tealet_stack_t *target, int fail_ok)
+{
+ while (list) {
+ int fail;
+ int full;
+ if (list == target) {
+ /* this is the stack we are switching to. We should stop here
+ * since previous stacks are already fully saved wrt. this.
+ * also, if this stack is not shared, it need not be saved
+ */
+ if (list->refcount > 1) {
+ /* saving because the target stack is shared. If failure cannot
+ * be handled, the target will be marked invalid on error. But
+ * the caller of this function will have already checked that
+ * and will complete the switch despite such a flag. Only
+ * subsequent uses of this stack will fail.
+ */
+ fail = tealet_stack_growto(main, list, saveto, &full, fail_ok);
+ if (fail)
+ return fail;
+ if (fail_ok)
+ assert(full); /* we saved it entirely */
+ }
+ tealet_stack_unlink(list);
+ return 0;
+ }
+
+ fail = tealet_stack_growto(main, list, saveto, &full, fail_ok);
+ if (fail)
+ return fail;
+ if (full)
+ tealet_stack_unlink(list);
+ list = list->next;
+ }
+ return 0;
+}
+
+/*********************************************************************
+ * the save and restore callbacks. These implement all the stack
+ * save and restore logic using previously defined functions
+ */
+
+/* main->g_target contains the tealet we are switching to:
+ * target->stack_far is the limit to which we must save the old stack
+ * target->stack can be NULL, indicating that the target stack
+ * needs not be restored.
+ */
+static void *tealet_save_state(void *old_stack_pointer, void *main)
+{
+ /* must free all the C stack up to target->stack_stop */
+ tealet_main_t *g_main = (tealet_main_t *)main;
+ tealet_sub_t *g_target = g_main->g_target;
+ tealet_sub_t *g_current = g_main->g_current;
+ char* target_stop = g_target->stack_far;
+ int exiting, fail, fail_ok;
+ assert(target_stop != NULL); /* target is't exiting */
+ assert(g_current != g_target);
+
+ exiting = g_current->stack_far == NULL;
+ fail_ok = !exiting;
+
+ /* save and unlink older stacks on demand */
+ /* when coming from main, there should be no list of unsaved stacks */
+ if (TEALET_IS_MAIN_STACK(g_main->g_current)) {
+ assert(!exiting);
+ assert(g_main->g_prev == NULL);
+ }
+ fail = tealet_stack_grow_list(g_main, g_main->g_prev, target_stop, g_target->stack, fail_ok);
+ if (fail)
+ return (void*) -1;
+ /* when returning to main, there should now be no list of unsaved stacks */
+ if (TEALET_IS_MAIN_STACK(g_main->g_target))
+ assert(g_main->g_prev == NULL);
+
+ if (exiting) {
+ /* tealet is exiting. We don't save its stack. */
+ assert(!TEALET_IS_MAIN_STACK(g_current));
+ if (g_current->stack == NULL) {
+ /* auto-delete the tealet */
+#ifndef NDEBUG
+ g_main->g_tealets--;
+#endif
+ tealet_int_free(g_main, g_current);
+ } else {
+ /* -1 means do-not-delete */
+ assert(g_current->stack == (tealet_stack_t*)-1);
+ g_current->stack = NULL;
+ }
+ } else {
+ /* save the initial stack chunk */
+ int full;
+ tealet_stack_t *stack = tealet_stack_saveto(g_main, (char*) old_stack_pointer,
+ g_current->stack_far, target_stop, &full);
+ if (!stack) {
+ if (fail_ok)
+ return (void*) -1;
+ assert(!TEALET_IS_MAIN_STACK(g_current));
+ g_current->stack = (tealet_stack_t *)-1; /* signal invalid stack */
+ } else {
+ g_current->stack = stack;
+ /* if it is partially saved, link it in to previous stacks */
+ if (TEALET_IS_MAIN_STACK(g_current))
+ assert(!full); /* always link in the main tealet's stack */
+ if (!full)
+ tealet_stack_link(stack, &g_main->g_prev);
+ }
+ }
+
+ if (g_target->stack == NULL)
+ return (void *) 1; /* don't restore */
+
+ /* return the stack pointer, it was saved here */
+ return g_target->stack->chunk.stack_near;
+}
+
+static void *tealet_restore_state(void *new_stack_pointer, void *main)
+{
+ tealet_main_t *g_main = (tealet_main_t *)main;
+ tealet_sub_t *g = g_main->g_target;
+
+ /* Restore the heap copy back into the C stack */
+ assert(g->stack != NULL);
+ tealet_stack_restore(g->stack);
+ tealet_stack_decref(g_main, g->stack);
+ g->stack = NULL;
+ return NULL;
+}
+
+static int tealet_switchstack(tealet_main_t *g_main)
+{
+ /* note: we can't pass g_target simply as an argument here, because
+ of the mix between different call stacks: after slp_switch() it
+ might end up with a different value. But g_main is safe, because
+ it should have always the same value before and after the switch. */
+ void *res;
+ assert(g_main->g_target);
+ assert(g_main->g_target != g_main->g_current);
+ /* if the target saved stack is invalid (due to a failure to save it
+ * during the exit of another tealet), we detect this here and
+ * report an error
+ * return value is:
+ * 0 = successful switch
+ * 1 = successful save only
+ * -1 = error, couldn't save state
+ * -2 = error, target tealet corrupt
+ */
+ if (g_main->g_target->stack == (tealet_stack_t*)-1)
+ return -2;
+ {
+ /* make sure that optimizers, e.g. gcc -O2, won't assume that
+ * g_main->g_target stays unchanged across the switch and optimize it
+ * into a register
+ */
+ tealet_sub_t * volatile *ptarget = &g_main->g_target;
+ res = slp_switch(tealet_save_state, tealet_restore_state, g_main);
+ g_main->g_target = *ptarget;
+ }
+ if ((int)res >= 0)
+ g_main->g_current = g_main->g_target;
+ g_main->g_target = NULL;
+ return (int)res;
+}
+
+/* We are initializing and switching to a new stub,
+ * in order to immediately start a new tealet's execution.
+ * stack_far is the far end of this stack and must be
+ * far enough that local variables in this function get saved.
+ * A stack variable in the calling function is sufficient.
+ */
+static int tealet_initialstub(tealet_main_t *g_main, tealet_run_t run, void *stack_far)
+{
+ int result;
+ tealet_sub_t *g = g_main->g_current;
+ tealet_sub_t *g_target = g_main->g_target;
+ assert(g_target->stack == NULL); /* it is fresh */
+
+ assert(run);
+ g_target->stack_far = (char *)stack_far;
+ result = _tealet_switchstack(g_main);
+ if (result < 0) {
+ /* couldn't allocate stack */
+ g_main->g_current = g;
+ return result;
+ }
+ if (result == 1) {
+ /* We successfully saved the source state (our caller) and initialized
+ * the current target without restoring state. We are the new tealet.
+ */
+ g = g_main->g_current;
+ assert(g == g_target);
+ assert(g->stack == NULL); /* running */
+
+ #ifdef DEBUG_DUMP
+ printf("starting %p\n", g);
+ #endif
+ g_target = (tealet_sub_t *)(run((tealet_t *)g, g_main->g_arg));
+ #ifdef DEBUG_DUMP
+ printf("ending %p -> %p\n", g, g_target);
+ #endif
+ if (tealet_exit((tealet_t*)g_target, NULL, TEALET_EXIT_DEFAULT))
+ tealet_exit((tealet_t*)g_main, NULL, TEALET_EXIT_DEFAULT); /* failsafe */
+ assert(!"This point should not be reached");
+ } else {
+ /* this is a switch back into the calling tealet */
+ assert(result == 0);
+ }
+ return 0;
+}
+
+static tealet_sub_t *tealet_alloc(tealet_main_t *g_main, tealet_alloc_t *alloc, size_t extrasize)
+{
+ tealet_sub_t *g;
+ size_t basesize = g_main == NULL ? offsetof(tealet_main_t, _extra) : offsetof(tealet_nonmain_t, _extra);
+ size_t size = basesize + extrasize;
+ if (g_main != NULL)
+ alloc = &g_main->g_alloc;
+ g = (tealet_sub_t*) alloc->malloc_p(size, alloc->context);
+ if (g == NULL)
+ return NULL;
+ if (g_main == NULL)
+ g_main = (tealet_main_t *)g;
+ g->base.main = (tealet_t *)g_main;
+ if (extrasize)
+ g->base.extra = (void*)((char*)g + basesize);
+ else
+ g->base.extra = NULL;
+ g->stack = NULL;
+ g->stack_far = NULL;
+#ifndef NDEBUG
+ g->id = g_main->g_counter++;
+#endif
+ return g;
+}
+
+
+/************************************************************/
+
+tealet_t *tealet_initialize(tealet_alloc_t *alloc, size_t extrasize)
+{
+ tealet_sub_t *g;
+ tealet_main_t *g_main;
+ g = tealet_alloc(NULL, alloc, extrasize);
+ if (g == NULL)
+ return NULL;
+ g_main = (tealet_main_t *)g;
+ g->stack = NULL;
+ g->stack_far = STACK_FAR_MAIN;
+ g_main->g_user = NULL;
+ g_main->g_current = g;
+ g_main->g_target = NULL;
+ g_main->g_arg = NULL;
+ g_main->g_alloc = *alloc;
+ g_main->g_prev = NULL;
+#ifndef NDEBUG
+ g_main->g_tealets = 0;
+ g_main->g_counter = 0;
+#endif
+ assert(TEALET_IS_MAIN_STACK(g_main));
+ /* set up the following field with an indirection, which is needed
+ to prevent any inlining */
+ _tealet_initialstub = tealet_initialstub;
+ _tealet_switchstack = tealet_switchstack;
+ return (tealet_t *)g_main;
+}
+
+void tealet_finalize(tealet_t *tealet)
+{
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ assert(TEALET_IS_MAIN_STACK(g_main));
+ assert(g_main->g_current == (tealet_sub_t *)g_main);
+ tealet_int_free(g_main, g_main);
+}
+
+void *tealet_malloc(tealet_t *tealet, size_t s)
+{
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ return tealet_int_malloc(g_main, s);
+}
+
+void tealet_free(tealet_t *tealet, void *p)
+{
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ tealet_int_free(g_main, p);
+}
+
+tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg, size_t extrasize)
+{
+ tealet_sub_t *result; /* store this until we return */
+ int fail;
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ assert(TEALET_IS_MAIN_STACK(g_main));
+ assert(!g_main->g_target);
+ result = tealet_alloc(g_main, NULL, extrasize);
+ if (result == NULL)
+ return NULL; /* Could not allocate */
+ g_main->g_target = result;
+ g_main->g_arg = parg ? *parg : NULL;
+#ifndef NDEBUG
+ g_main->g_tealets ++;
+#endif
+ fail = _tealet_initialstub(g_main, run, (void*)&result);
+ if (fail) {
+ /* could not save stack */
+ tealet_int_free(g_main, result);
+ g_main->g_target = NULL;
+#ifndef NDEBUG
+ g_main->g_tealets --;
+#endif
+ return NULL;
+ }
+ if (parg)
+ *parg = g_main->g_arg;
+ return (tealet_t*)result;
+}
+
+int tealet_switch(tealet_t *stub, void **parg)
+{
+ tealet_sub_t *g_target = (tealet_sub_t *)stub;
+ tealet_main_t *g_main = TEALET_GET_MAIN(g_target);
+ int result;
+ if (g_target == g_main->g_current)
+ return 0; /* switch to self */
+#ifdef DEBUG_DUMP
+ printf("switch %p -> %p\n", g_main->g_current, g_target);
+#endif
+ g_main->g_target = g_target;
+ g_main->g_arg = parg ? *parg : NULL;
+ result = _tealet_switchstack(g_main);
+ if (parg)
+ *parg = g_main->g_arg;
+#ifdef DEBUG_DUMP
+ printf("done switch, res=%d, now in %p\n", result, g_main->g_current);
+#endif
+ return result;
+}
+
+int tealet_exit(tealet_t *target, void *arg, int flags)
+{
+ tealet_sub_t *g_target = (tealet_sub_t *)target;
+ tealet_main_t *g_main = TEALET_GET_MAIN(g_target);
+ tealet_sub_t *g_current = g_main->g_current;
+ char *stack_far = g_target->stack_far;
+ int result;
+ assert(g_current != (tealet_sub_t*)g_main); /* mustn't exit main */
+ if (g_target == g_current)
+ return -2; /* invalid tealet */
+
+ g_current->stack_far = NULL; /* signal exit */
+ assert (g_current->stack == NULL);
+ if (flags & TEALET_EXIT_NODELETE)
+ g_current->stack = (tealet_stack_t*) -1; /* signal do-not-delete */
+ g_main->g_target = g_target;
+ g_main->g_arg = arg;
+ result = _tealet_switchstack(g_main);
+ assert(result < 0); /* only return here if there was failure */
+ g_target->stack_far = stack_far;
+ g_current->stack = NULL;
+ return result;
+}
+
+tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize)
+{
+ tealet_sub_t *g_tealet = (tealet_sub_t *)tealet;
+ tealet_main_t *g_main = TEALET_GET_MAIN(g_tealet);
+ tealet_sub_t *g_copy;
+ /* can't dup the current or the main tealet */
+ assert(g_tealet != g_main->g_current && g_tealet != (tealet_sub_t*)g_main);
+ g_copy = tealet_alloc(g_main, NULL, extrasize);
+ if (g_copy == NULL)
+ return NULL;
+#ifndef NDEBUG
+ g_main->g_tealets++;
+#endif
+ /* copy the relevant bits. extra data is not copied since we don't
+ * know how large it was in the source
+ */
+ g_copy->stack_far = g_tealet->stack_far;
+ g_copy->stack = tealet_stack_dup(g_tealet->stack);
+ return (tealet_t*)g_copy;
+}
+
+void tealet_delete(tealet_t *target)
+{
+ tealet_sub_t *g_target = (tealet_sub_t *)target;
+ tealet_main_t *g_main = TEALET_GET_MAIN(g_target);
+ assert(!TEALET_IS_MAIN(target));
+ tealet_stack_decref(g_main, g_target->stack);
+ tealet_int_free(g_main, g_target);
+#ifndef NDEBUG
+ g_main->g_tealets--;
+#endif
+}
+
+tealet_t *tealet_current(tealet_t *tealet)
+{
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ return (tealet_t *)g_main->g_current;
+}
+
+void **tealet_main_userpointer(tealet_t *tealet)
+{
+ tealet_main_t *g_main = TEALET_GET_MAIN(tealet);
+ return &g_main->g_user;
+}
+
+int tealet_status(tealet_t *_tealet)
+{
+ tealet_sub_t *tealet = (tealet_sub_t *)_tealet;
+ if (tealet->stack_far == NULL)
+ return TEALET_STATUS_EXITED;
+ if (tealet->stack == (tealet_stack_t*)-1)
+ return TEALET_STATUS_DEFUNCT;
+ return TEALET_STATUS_ACTIVE;
+}
+
+#ifndef NDEBUG
+int tealet_get_count(tealet_t *tealet)
+{
+ return TEALET_GET_MAIN(tealet)->g_tealets;
+}
+#endif
+
+ptrdiff_t tealet_stack_diff(void *a, void *b)
+{
+ return STACK_SUB((ptrdiff_t)a, (ptrdiff_t)(b));
+}
+
+void *tealet_get_far(tealet_t *_tealet)
+{
+ tealet_sub_t *tealet = (tealet_sub_t *)_tealet;
+ return tealet->stack_far;
+}
+
+void *tealet_new_far(tealet_t *d1, tealet_run_t d2, void **d3, size_t d4)
+{
+ tealet_sub_t *result;
+ void *r;
+ (void)d1;
+ (void)d2;
+ (void)d3;
+ (void)d4;
+ /* avoid compiler warnings about returning tmp addr */
+ r = (void*)&result;
+ return r;
+}
diff --git a/Stackless/tealet/tealet.h b/Stackless/tealet/tealet.h
new file mode 100644
index 00000000000000..beeebcfec3d411
--- /dev/null
+++ b/Stackless/tealet/tealet.h
@@ -0,0 +1,227 @@
+/********** A minimal coroutine package for C **********/
+#ifndef _TEALET_H_
+#define _TEALET_H_
+
+#include
+
+#ifdef WIN32
+#if defined TEALET_EXPORTS
+#define TEALET_API __declspec(dllexport)
+#elif defined TEALET_IMPORTS
+#define TEALET_API __declspec(dllimport)
+#else
+#define TEALET_API
+#endif
+#else /* win32 */
+#define TEALET_API
+#endif
+
+
+/* A structure to define the memory allocation api used.
+ * the functions have C89 semantics and take an additional "context"
+ * pointer that they can use as they please
+ */
+typedef void*(*tealet_malloc_t)(size_t size, void *context);
+typedef void*(*tealet_free_t)(void *ptr, void *context);
+typedef struct tealet_alloc_t {
+ tealet_malloc_t malloc_p;
+ tealet_free_t free_p;
+ void *context;
+} tealet_alloc_t;
+
+/* use the following macro to initialize a tealet_alloc_t
+ * structure with stdlib malloc functions, for convenience, e.g.:
+ * tealet_alloc_t stdalloc = TEALET_MALLOC;
+ */
+#define TEALET_MALLOC {\
+ (tealet_malloc_t)&malloc, \
+ (tealet_free_t)&free, \
+ 0 \
+}
+
+
+/* The user-visible tealet structure. If an "extrasize" is provided when
+ * the tealet is created, "extra" points to a block of that size, otherwise
+ * it is initialized to NULL
+ */
+typedef struct tealet_t {
+ struct tealet_t *main; /* pointer to the main tealet */
+ void *extra;
+ /* private fields follow */
+} tealet_t;
+
+/* The "run" function of a tealet. It is called with the
+ * current tealet and the argument provided to its start function
+ */
+typedef tealet_t *(*tealet_run_t)(tealet_t *current, void *arg);
+
+
+/* error codes. API functions that return int return a negative value
+ * to signal an error.
+ * Those that return tealet_t pointers return NULL to signal a memory
+ * error.
+ */
+#define TEALET_ERR_MEM -1 /* memory allocation failed */
+#define TEALET_ERR_DEFUNCT -2 /* the target tealet is corrupt */
+
+
+/* Initialize and return the main tealet. The main tealet contains the whole
+ * "normal" execution of the program; it starts when the program starts and
+ * ends when the program ends. This function and tealet_finalize() should
+ * be called together from the same (main) function which calls the rest of
+ * the program. It is fine to nest several uses of initialize/finalize,
+ * or to call them in multiple threads in case of multithreaded programs,
+ * as long as you don't try to switch between tealets created with a
+ * different main tealet.
+ */
+TEALET_API
+tealet_t *tealet_initialize(tealet_alloc_t *alloc, size_t extrasize);
+
+/* Tear down the main tealet. Call e.g. after a thread finishes (including
+ * all its tealets).
+ */
+TEALET_API
+void tealet_finalize(tealet_t *tealet);
+
+/* access to the tealet's allocator. This can be useful to use
+ * e.g. when passing data between tealets but such data cannot
+ * reside on the stack (except during the initial call to tealet_new)
+ */
+TEALET_API
+void *tealet_malloc(tealet_t *tealet, size_t s);
+TEALET_API
+void tealet_free(tealet_t *tealet, void *p);
+
+/* Allocate a new tealet 'g', and call 'run(g, *arg)' in it.
+ * The return value of run() must be the next tealet in which to continue
+ * execution, which must be a different one, like for example the main tealet.
+ * When 'run(g)' returns, the tealet 'g' is freed.
+ * The return value is the new tealet, or NULL if memory allocation failed.
+ * Note that this tealet may have been alread freed should run(g) have
+ * returned by the time this function returns.
+ * On return, *arg contains the arg value passed in to the switch
+ * causing this return.
+ * 'arg' can be NULL, in which case NULL is passed to run and no result
+ * argument is passed.
+ * If 'extrasize' is non-zero, extra data will be allocated and the tealet's
+ * 'extra' member points to it, otherwise it is set to NULL
+ */
+TEALET_API
+tealet_t *tealet_new(tealet_t *tealet, tealet_run_t run, void **parg, size_t extrasize);
+
+/* Switch to another tealet. Execution continues there. The tealet
+ * passed in must not have been freed yet and must descend from
+ * the same main tealet as the current one. In multithreaded applications,
+ * it must also belong to the current thread (otherwise, segfaults).
+ * if 'arg' is non-NULL, the argument passed in *arg will be provided
+ * to the other tealet when it returns from its tealet_new() or
+ * tealet_switch().
+ * On return, *arg contains whatever *arg was
+ * provided when switching back here.
+ * Take care to not have *arg point to stack allocated data because
+ * such data may be overwritten when the context switches.
+ */
+TEALET_API
+int tealet_switch(tealet_t *target, void **parg);
+
+/* Exit the current tealet. Similar to tealet_switch except that it only
+ * ever returns if the target tealet is defunct.
+ * It also allows passing of an *arg to the target tealet, plus allows
+ * control over whether the tealet is automatically deleted or not.
+ * Returning with 'p' from the tealet's run funciton is equivalent to calling
+ * tealet_exit(p, NULL, 1).
+ * This function can be used as an emergency measure to return to the
+ * main tealet if tealet_switch() fails due to inability to save the stack.
+ * Note that exiting to the main tealet is always guaranteed to work.
+ */
+#define TEALET_EXIT_DEFAULT 0
+#define TEALET_EXIT_NODELETE 1
+TEALET_API
+int tealet_exit(tealet_t *target, void *arg, int flags);
+
+/* Duplicate a tealet. The active tealet is duplicated
+ * along with its stack contents.
+ * This can be used, for example, to create "stubs" that can be duplicated
+ * and re-used to run with different arguments.
+ * Use this with care, because initially the duplicate will share the same
+ * stack data as the original. This includes any local variables, even the
+ * "current" argument passed to the original "run" function which may
+ * be obsolete by the time the duplicate is run. Use the argument passing
+ * mechanism to provide the copy with fresh data.
+ */
+TEALET_API
+tealet_t *tealet_duplicate(tealet_t *tealet, size_t extrasize);
+
+/* Deallocate a tealet. Use this to delete a tealet that has exited
+ * with tealet_exit() with 'TEALET_EXIT_NODELETE', or defunct tealets.
+ * Active tealet can also be
+ * deleted, such as stubs that are no longer in use, but take care
+ * because any local resources in such tealets won't be freed.
+ */
+TEALET_API
+void tealet_delete(tealet_t *target);
+
+/* Return the current tealet, i.e. the one in which the caller of this
+ * function currently is. "tealet" can be any tealet derived from the
+ * main tealet.
+ */
+TEALET_API
+tealet_t *tealet_current(tealet_t *tealet);
+
+/* Get the address of a the tealet's main user pointer, a single
+ * void pointer associated with the main tealet. Use this to
+ * associate a (void*)value with the main tealet.
+ */
+TEALET_API
+void **tealet_main_userpointer(tealet_t *tealet);
+
+/* functions for stack arithmetic. Useful when deciding
+ * if you want to start a newa tealet, or when doing stack
+ * spilling
+ */
+
+/* subtract two stack positions, taking into account if the
+ * local stack grows up or down in memory.
+ * The result is positive if 'b' is 'deeper' on the stack
+ * than 'a'
+ */
+TEALET_API
+ptrdiff_t tealet_stack_diff(void *a, void *b);
+
+/* Get a tealet's "far" position on the stack. This is an
+ * indicator of its creation position on the stack. The main
+ * tealet extends until the beginning of stack
+ */
+TEALET_API
+void *tealet_get_far(tealet_t *_tealet);
+
+/* this is used to get the "far address _if_ a tealet were initialized here
+ * The arguments must match the real tealet_new() but are dummies.
+ */
+TEALET_API
+void *tealet_new_far(tealet_t *dummy1, tealet_run_t dummy2, void **dummy3, size_t dummy4);
+
+/* get a tealet's status */
+#define TEALET_STATUS_ACTIVE 0
+#define TEALET_STATUS_EXITED 1
+#define TEALET_STATUS_DEFUNCT -2
+TEALET_API
+int tealet_status(tealet_t *tealet);
+
+#ifndef NDEBUG
+TEALET_API
+int tealet_get_count(tealet_t *t);
+#endif
+
+/* Convenience macros */
+#define TEALET_MAIN(t) ((t)->main)
+#define TEALET_IS_MAIN(t) ((t) == TEALET_MAIN(t))
+#define TEALET_CURRENT_IS_MAIN(t) (tealet_current(t) == TEALET_MAIN(t))
+
+/* see if two tealets share the same MAIN, and can therefore be switched between */
+#define TEALET_RELATED(t1, t2) (TEALET_MAIN(t1) == TEALET_MAIN(t2))
+
+/* convenience access to a typecast extra pointer */
+#define TEALET_EXTRA(t, tp) ((tp*)((t)->extra))
+
+#endif /* _TEALET_H_ */
diff --git a/Stackless/tealet/tealet.sln b/Stackless/tealet/tealet.sln
new file mode 100644
index 00000000000000..7958176dfa8edd
--- /dev/null
+++ b/Stackless/tealet/tealet.sln
@@ -0,0 +1,36 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tealet", "tealet.vcxproj", "{4D8AC602-C828-4EDC-B65A-9A439810F157}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcxproj", "{E03E9A66-978B-43DE-82D6-FE0F6367368F}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.Build.0 = Debug|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.ActiveCfg = Debug|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.Build.0 = Debug|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.ActiveCfg = Release|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.Build.0 = Release|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.ActiveCfg = Release|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.Build.0 = Release|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.Build.0 = Debug|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.ActiveCfg = Debug|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.Build.0 = Debug|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.ActiveCfg = Release|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.Build.0 = Release|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.ActiveCfg = Release|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Stackless/tealet/tealet.vcproj b/Stackless/tealet/tealet.vcproj
new file mode 100644
index 00000000000000..c2dca5bb195ba2
--- /dev/null
+++ b/Stackless/tealet/tealet.vcproj
@@ -0,0 +1,454 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Stackless/tealet/tealet.vcxproj b/Stackless/tealet/tealet.vcxproj
new file mode 100644
index 00000000000000..9be8abf16525d3
--- /dev/null
+++ b/Stackless/tealet/tealet.vcxproj
@@ -0,0 +1,215 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}
+ tealet
+ Win32Proj
+
+
+
+ DynamicLibrary
+ Unicode
+ true
+
+
+ DynamicLibrary
+ Unicode
+
+
+ DynamicLibrary
+ Unicode
+ true
+
+
+ DynamicLibrary
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.40219.1
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ true
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ true
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ false
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ EditAndContinue
+
+
+ true
+ Windows
+ MachineX86
+
+
+
+
+ X64
+
+
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Windows
+ MachineX64
+
+
+
+
+ MaxSpeed
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+
+
+ Level3
+ ProgramDatabase
+
+
+ false
+ true
+ Windows
+ true
+ true
+ MachineX86
+
+
+
+
+ X64
+
+
+ MaxSpeed
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;TEALET_EXPORTS;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Windows
+ true
+ true
+ MachineX64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ Assembling
+ ml64 /nologo /c /Zi /Fo "$(IntDir)%(Filename) .obj" "%(FullPath)"
+
+ $(IntDir)%(Filename) .obj;%(Outputs)
+ true
+ Assembling
+ ml64 /nologo /c /Zi /Fo "$(IntDir)%(Filename) .obj" "%(FullPath)"
+
+ $(IntDir)%(Filename) .obj;%(Outputs)
+
+
+ Assembling
+ ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)"
+
+ $(IntDir)%(Filename).obj;%(Outputs)
+ true
+ ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)"
+
+ $(IntDir)%(Filename).obj;%(Outputs)
+ Assembling
+ ml -c -Zi "-Fl$(IntDir)%(Filename).lst" "-Fo$(IntDir)%(Filename).obj" "%(FullPath)"
+
+ $(IntDir)%(Filename).obj;%(Outputs)
+ true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Stackless/tealet/tealetvs2008.sln b/Stackless/tealet/tealetvs2008.sln
new file mode 100644
index 00000000000000..eb422edf285782
--- /dev/null
+++ b/Stackless/tealet/tealetvs2008.sln
@@ -0,0 +1,39 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual Studio 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tealet", "tealet.vcproj", "{4D8AC602-C828-4EDC-B65A-9A439810F157}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test.vcproj", "{E03E9A66-978B-43DE-82D6-FE0F6367368F}"
+ ProjectSection(ProjectDependencies) = postProject
+ {4D8AC602-C828-4EDC-B65A-9A439810F157} = {4D8AC602-C828-4EDC-B65A-9A439810F157}
+ EndProjectSection
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.ActiveCfg = Debug|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|Win32.Build.0 = Debug|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.ActiveCfg = Debug|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Debug|x64.Build.0 = Debug|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.ActiveCfg = Release|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|Win32.Build.0 = Release|Win32
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.ActiveCfg = Release|x64
+ {4D8AC602-C828-4EDC-B65A-9A439810F157}.Release|x64.Build.0 = Release|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.ActiveCfg = Debug|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|Win32.Build.0 = Debug|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.ActiveCfg = Debug|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Debug|x64.Build.0 = Debug|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.ActiveCfg = Release|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|Win32.Build.0 = Release|Win32
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.ActiveCfg = Release|x64
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Stackless/tealet/test.vcproj b/Stackless/tealet/test.vcproj
new file mode 100644
index 00000000000000..3cf8e75d77f51c
--- /dev/null
+++ b/Stackless/tealet/test.vcproj
@@ -0,0 +1,358 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Stackless/tealet/test.vcxproj b/Stackless/tealet/test.vcxproj
new file mode 100644
index 00000000000000..95aedadedaa844
--- /dev/null
+++ b/Stackless/tealet/test.vcxproj
@@ -0,0 +1,184 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {E03E9A66-978B-43DE-82D6-FE0F6367368F}
+ test
+ Win32Proj
+
+
+
+ Application
+ Unicode
+ true
+
+
+ Application
+ Unicode
+
+
+ Application
+ Unicode
+ true
+
+
+ Application
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>10.0.40219.1
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ true
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ true
+ $(SolutionDir)$(Configuration)\
+ $(Configuration)\
+ false
+ $(SolutionDir)$(Platform)\$(Configuration)\
+ $(Platform)\$(Configuration)\
+ false
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+ AllRules.ruleset
+
+
+
+
+
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;TEALET_IMPORTS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ EditAndContinue
+
+
+ true
+ Console
+ MachineX86
+
+
+
+
+ X64
+
+
+ Disabled
+ WIN32;_DEBUG;_CONSOLE;TEALET_IMPORTS;%(PreprocessorDefinitions)
+ true
+ EnableFastChecks
+ MultiThreadedDebugDLL
+
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Console
+ MachineX64
+
+
+
+
+ MaxSpeed
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Console
+ true
+ true
+ MachineX86
+
+
+
+
+ X64
+
+
+ MaxSpeed
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ MultiThreadedDLL
+ true
+
+
+ Level3
+ ProgramDatabase
+
+
+ true
+ Console
+ true
+ true
+ MachineX64
+
+
+
+
+
+
+
+ {4d8ac602-c828-4edc-b65a-9a439810f157}
+ false
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Stackless/tealet/tests.c b/Stackless/tealet/tests.c
new file mode 100644
index 00000000000000..ff9c51774e8160
--- /dev/null
+++ b/Stackless/tealet/tests.c
@@ -0,0 +1,621 @@
+#include
+#include
+#include
+#include "tealet.h"
+
+static int status = 0;
+static tealet_t *g_main = NULL;
+static tealet_t *the_stub = NULL;
+static int newmode = 0;
+
+
+static tealet_alloc_t talloc = TEALET_MALLOC;
+void init_test() {
+ assert(g_main == NULL);
+ g_main = tealet_initialize(&talloc);
+ assert(tealet_current(g_main) == g_main);
+ status = 0;
+}
+void fini_test() {
+ assert(g_main != NULL);
+ assert(tealet_current(g_main) == g_main);
+ if (the_stub)
+ tealet_delete(the_stub);
+ the_stub = NULL;
+#ifndef NDEBUG
+ assert(tealet_get_count(g_main) == 0);
+#endif
+ tealet_finalize(g_main);
+ g_main = NULL;
+}
+
+/****************************************************************
+ *Implement copyable stubs by using a trampoline
+ */
+struct mystub_arg
+{
+ tealet_t *current;
+ tealet_run_t run;
+ void *runarg;
+};
+static tealet_t *mystub_main(tealet_t *current, void *arg)
+{
+ void *myarg = 0;
+ /* the caller is in arg, return right back to him */
+ tealet_switch((tealet_t*)arg, &myarg);
+ /* now we are back, myarg should contain the arg to the run function.
+ * We were possibly duplicated, so can't trust the original function args.
+ */
+ {
+ struct mystub_arg sarg = *(struct mystub_arg*)myarg;
+ tealet_free(sarg.current, myarg);
+ return (sarg.run)(sarg.current, sarg.runarg);
+ }
+}
+
+/* create a stub and return it */
+tealet_t *mystub_new(tealet_t *t) {
+ void *arg = (void*)tealet_current(t);
+ return tealet_new(t, mystub_main, &arg);
+}
+
+/* run a stub */
+int mystub_run(tealet_t *stub, tealet_run_t run, void **parg)
+{
+ int result;
+ void *myarg;
+ /* we cannot pass arguments to a different tealet on the stack */
+ struct mystub_arg *psarg = (struct mystub_arg*)tealet_malloc(stub, sizeof(struct mystub_arg));
+ if (!psarg)
+ return TEALET_ERR_MEM;
+ psarg->current = stub;
+ psarg->run = run;
+ psarg->runarg = parg ? *parg : NULL;
+ myarg = (void*)psarg;
+ result = tealet_switch(stub, &myarg);
+ if (result) {
+ /* failure */
+ tealet_free(stub, psarg);
+ return result;
+ }
+ /* pass back the arg value from the switch */
+ if (parg)
+ *parg = myarg;
+ return 0;
+}
+
+
+
+/**************************************/
+
+/* create a tealet or stub low on the stack */
+static tealet_t * tealet_new_descend(tealet_t *t, int level, tealet_run_t run, void **parg)
+{
+ int boo[10];
+ boo[9] = 0;
+ if (level > 0)
+ return tealet_new_descend(t, level-1, run, parg);
+ if (run)
+ return tealet_new(t, run, parg);
+ else
+ return mystub_new(t);
+}
+
+/***************************************
+ * methods for creating tealets in different ways
+ */
+
+static tealet_t * tealet_new_rnd(tealet_t* t, tealet_run_t run, void **parg)
+{
+ return tealet_new_descend(t, rand() % 20, run, parg);
+}
+
+static tealet_t * stub_new(tealet_t *t, tealet_run_t run, void **parg)
+{
+ tealet_t *stub = tealet_new_descend(t, rand() % 20, NULL, NULL);
+ int res;
+ if (stub == NULL)
+ return NULL;
+ if (run)
+ res = mystub_run(stub, run, parg);
+ else
+ res = 0;
+ if (res) {
+ tealet_delete(stub);
+ assert(res == TEALET_ERR_MEM);
+ return NULL;
+ }
+ return stub;
+}
+
+static tealet_t * stub_new2(tealet_t *t, tealet_run_t run, void **parg)
+{
+ tealet_t *dup, *stub;
+ int res;
+ stub = tealet_new_descend(t, rand() % 20, NULL, NULL);
+ if (stub == NULL)
+ return NULL;
+ dup = tealet_duplicate(stub);
+ if (stub == NULL) {
+ tealet_delete(stub);
+ return NULL;
+ }
+ if (run)
+ res = mystub_run(dup, run, parg);
+ else
+ res = 0;
+ tealet_delete(stub);
+ if (res) {
+ tealet_delete(dup);
+ assert(res == TEALET_ERR_MEM);
+ return NULL;
+ }
+ return dup;
+}
+
+static tealet_t * stub_new3(tealet_t *t, tealet_run_t run, void **parg)
+{
+ tealet_t *dup;
+ int res;
+ if ((rand()%10) == 0)
+ if (the_stub != NULL) {
+ tealet_delete(the_stub);
+ the_stub = NULL;
+ }
+ if (the_stub == NULL)
+ the_stub = tealet_new_descend(t, rand() % 20, NULL, NULL);
+ if (the_stub == NULL)
+ return NULL;
+ dup = tealet_duplicate(the_stub);
+ if (dup == NULL)
+ return NULL;
+ if (run) {
+ res = mystub_run(dup, run, parg);
+ if (res) {
+ tealet_delete(dup);
+ assert(res == TEALET_ERR_MEM);
+ return NULL;
+ }
+ }
+ return dup;
+}
+
+
+typedef tealet_t* (*t_new)(tealet_t *, tealet_run_t, void**);
+static t_new newarray[] = {tealet_new_rnd, stub_new, stub_new2, stub_new3};
+
+static t_new get_new(){
+ if (newmode >= 0)
+ return newarray[newmode];
+ return newarray[rand() % (sizeof(newarray)/sizeof(*newarray))];
+}
+#define tealet_new get_new()
+
+/************************************************************/
+
+void test_main_current(void)
+{
+ init_test();
+ fini_test();
+}
+
+/************************************************************/
+
+tealet_t *test_simple_run(tealet_t *t1, void *arg)
+{
+ assert(t1 != g_main);
+ status = 1;
+ return g_main;
+}
+
+void test_simple(void)
+{
+ init_test();
+ tealet_new(g_main, test_simple_run, NULL);
+ assert(status == 1);
+ fini_test();
+}
+
+/*************************************************************/
+
+tealet_t *test_status_run(tealet_t *t1, void *arg)
+{
+ assert(t1 == tealet_current(t1));
+ assert(!TEALET_IS_MAIN(t1));
+ assert(tealet_status(t1) == TEALET_STATUS_ACTIVE);
+ return g_main;
+}
+
+void test_status(void)
+{
+ tealet_t *stub1;
+ init_test();
+
+ assert(tealet_status(g_main) == TEALET_STATUS_ACTIVE);
+ assert(TEALET_IS_MAIN(g_main));
+
+ stub1 = tealet_new(g_main, NULL, NULL);
+ assert(tealet_status(stub1) == TEALET_STATUS_ACTIVE);
+ assert(!TEALET_IS_MAIN(stub1));
+ mystub_run(stub1, test_status_run, NULL);
+
+ fini_test();
+}
+
+
+/*************************************************************/
+tealet_t *test_exit_run(tealet_t *t1, void *arg)
+{
+ int result;
+ assert(t1 != g_main);
+ status += 1;
+ result = tealet_exit(g_main, NULL, (int)arg);
+ assert(0);
+ return (tealet_t*)-1;
+}
+
+void test_exit(void)
+{
+ tealet_t *stub1, *stub2;
+ int result;
+ void *arg;
+ init_test();
+ stub1 = tealet_new(g_main, NULL, NULL);
+ stub2 = tealet_duplicate(stub1);
+ arg = (void*)TEALET_EXIT_NODELETE;
+ result = mystub_run(stub1, test_exit_run, &arg);
+ assert(result == 0);
+ assert(status == 1);
+ assert(tealet_status(stub1) == TEALET_STATUS_EXITED);
+ tealet_delete(stub1);
+ arg = (void*)TEALET_EXIT_DEFAULT;
+ result = mystub_run(stub2, test_exit_run, &arg);
+ assert(status == 2);
+ fini_test();
+}
+
+
+/************************************************************/
+
+static tealet_t *glob_t1;
+static tealet_t *glob_t2;
+
+tealet_t *test_switch_2(tealet_t *t2, void *arg)
+{
+ assert(t2 != g_main);
+ assert(t2 != glob_t1);
+ glob_t2 = t2;
+ assert(status == 1);
+ status = 2;
+ assert(tealet_current(g_main) == t2);
+ tealet_switch(glob_t1, NULL);
+ assert(status == 3);
+ status = 4;
+ assert(tealet_current(g_main) == t2);
+ tealet_switch(glob_t1, NULL);
+ assert(status == 5);
+ status = 6;
+ assert(t2 == glob_t2);
+ assert(tealet_current(g_main) == t2);
+ tealet_switch(t2, NULL);
+ assert(status == 6);
+ status = 7;
+ assert(tealet_current(g_main) == t2);
+ return g_main;
+}
+
+tealet_t *test_switch_1(tealet_t *t1, void *arg)
+{
+ assert(t1 != g_main);
+ glob_t1 = t1;
+ assert(status == 0);
+ status = 1;
+ assert(tealet_current(g_main) == t1);
+ tealet_new(g_main, test_switch_2, NULL);
+ assert(status == 2);
+ status = 3;
+ assert(tealet_current(g_main) == t1);
+ tealet_switch(glob_t2, NULL);
+ assert(status == 4);
+ status = 5;
+ assert(tealet_current(g_main) == t1);
+ return glob_t2;
+}
+
+void test_switch(void)
+{
+ init_test();
+ tealet_new(g_main, test_switch_1, NULL);
+ assert(status == 7);
+ fini_test();
+}
+
+/************************************************************/
+
+/* 1 is high on the stack. We then create 2 lower on the stack */
+/* the execution is : m 1 m 2 1 m 2 m */
+tealet_t *test_switch_new_1(tealet_t *t1, void *arg)
+{
+ tealet_t *caller = (tealet_t*)arg;
+ tealet_t *stub;
+ /* switch back to the creator */
+ tealet_switch(caller, NULL);
+ /* now we want to trample the stack */
+ stub = tealet_new_descend(t1, 50, NULL, NULL);
+ tealet_delete(stub);
+ /* and back to main */
+ return g_main;
+}
+
+tealet_t *test_switch_new_2(tealet_t *t2, void *arg) {
+ tealet_t *target = (tealet_t*)arg;
+ /* switch to tealet 1 to trample the stack*/
+ target->data = (void*)t2;
+ tealet_switch(target, NULL);
+
+ /* and then return to main */
+ return g_main;
+}
+
+void test_switch_new(void)
+{
+ tealet_t *tealet1, *tealet2;
+ void *arg;
+ init_test();
+ arg = (void *)tealet_current(g_main);
+ tealet1 = tealet_new(g_main, test_switch_new_1, &arg);
+ /* the tealet is now running */
+ arg = (void*)tealet1;
+ tealet2 = tealet_new_descend(g_main, 4, test_switch_new_2, &arg);
+ assert(tealet_status(tealet2) == TEALET_STATUS_ACTIVE);
+ tealet_switch(tealet2, NULL);
+ /* tealet should be dead now */
+ fini_test();
+}
+
+/************************************************************/
+
+/* test argument passing with switch and exit */
+tealet_t *test_arg_1(tealet_t *t1, void *arg)
+{
+ void *myarg;
+ tealet_t *peer = (tealet_t*)arg;
+ myarg = (void*)1;
+ tealet_switch(peer, &myarg);
+ assert(myarg == (void*)2);
+ myarg = (void*)3;
+ tealet_exit(peer, myarg, TEALET_EXIT_DEFAULT);
+ return NULL;
+}
+
+void test_arg(void)
+{
+ void *myarg;
+ tealet_t *t1;
+ init_test();
+ myarg = (void*)g_main;
+ t1 = tealet_new(g_main, test_arg_1, &myarg);
+ assert(myarg == (void*)1);
+ myarg = (void*)2;
+ tealet_switch(t1, &myarg);
+ assert(myarg == (void*)3);
+ fini_test();
+}
+
+/************************************************************/
+
+#define ARRAYSIZE 127
+#define MAX_STATUS 50000
+
+static tealet_t *tealetarray[ARRAYSIZE] = {NULL};
+static int got_index;
+
+tealet_t *random_new_tealet(tealet_t*, void *arg);
+
+static void random_run(int index)
+{
+ int i, prevstatus;
+ void *arg;
+ tealet_t *cur = tealet_current(g_main);
+ assert(tealetarray[index] == cur);
+ do
+ {
+ i = rand() % (ARRAYSIZE + 1);
+ status += 1;
+ if (i == ARRAYSIZE)
+ break;
+ prevstatus = status;
+ got_index = i;
+ if (tealetarray[i] == NULL)
+ {
+ if (status >= MAX_STATUS)
+ break;
+ arg = (void*)i;
+ tealet_new(g_main, random_new_tealet, &arg);
+ }
+ else
+ {
+ tealet_switch(tealetarray[i], NULL);
+ }
+ assert(status >= prevstatus);
+ assert(tealet_current(g_main) == cur);
+ assert(tealetarray[index] == cur);
+ assert(got_index == index);
+ }
+ while (status < MAX_STATUS);
+}
+
+tealet_t *random_new_tealet(tealet_t* cur, void *arg)
+{
+ int i = got_index;
+ assert(tealet_current(g_main) == cur);
+ assert(i == (int)(arg));
+ assert(i > 0 && i < ARRAYSIZE);
+ assert(tealetarray[i] == NULL);
+ tealetarray[i] = cur;
+ random_run(i);
+ tealetarray[i] = NULL;
+
+ i = rand() % ARRAYSIZE;
+ if (tealetarray[i] == NULL)
+ {
+ assert(tealetarray[0] != NULL);
+ i = 0;
+ }
+ got_index = i;
+ return tealetarray[i];
+}
+
+void test_random(void)
+{
+ int i;
+ init_test();
+ for( i=0; i 0 && index < ARRAYSIZE);
+ assert(tealetarray[index] == NULL);
+ tealetarray[index] = cur;
+ random2_run(index);
+ tealetarray[index] = NULL;
+ return tealetarray[0]; /* switch to main */
+}
+void random2_new(int index) {
+ void *arg = (void*)index;
+ tealet_new(g_main, random2_tealet, &arg);
+}
+
+int random2_descend(int index, int level) {
+ int target;
+ if (level > 0)
+ return random2_descend(index, level-1);
+
+ /* find target */
+ target = rand() % ARRAYSIZE;
+ if (status