diff --git a/Include/cpython/slp_structs.h b/Include/cpython/slp_structs.h index e915565bbd784bc..37d02cadc60a874 100644 --- a/Include/cpython/slp_structs.h +++ b/Include/cpython/slp_structs.h @@ -101,16 +101,6 @@ typedef struct _tasklet_flags { unsigned int pending_irq: SLP_TASKLET_FLAGS_BITS_pending_irq; } PyTaskletFlagStruc; -/* a partial copy of PyThreadState. Used to preserve - the state of a hard switched tasklet */ -typedef struct _tasklet_tstate { - int tracing; - Py_tracefunc c_profilefunc; - Py_tracefunc c_tracefunc; - PyObject *c_profileobj; - PyObject *c_traceobj; -} PyTaskletTStateStruc; - typedef struct _tasklet { PyObject_HEAD struct _tasklet *next; @@ -133,9 +123,20 @@ typedef struct _tasklet { int recursion_depth; PyObject *def_globals; PyObject *tsk_weakreflist; - /* If the tasklet is current: NULL. (The context of a current tasklet is always in ts->tasklet.) + /* If the tasklet is current: NULL. (The context of a current tasklet is + * always in ts->context.) * If the tasklet is not current: the context for the tasklet */ PyObject *context; + + /* If the tasklet is current: NULL. (The profile and tracing state of a + * current tasklet is stored in the thread state.) + * If the tasklet is not current: the profile and tracing state for the tasklet. + */ + Py_tracefunc profilefunc; + Py_tracefunc tracefunc; + PyObject *profileobj; + PyObject *traceobj; + int tracing; } PyTaskletObject; diff --git a/Include/internal/pycore_stackless.h b/Include/internal/pycore_stackless.h index 8562d915a2ce83c..a9844ada69a3928 100644 --- a/Include/internal/pycore_stackless.h +++ b/Include/internal/pycore_stackless.h @@ -380,8 +380,6 @@ PyAPI_FUNC(PyObject *) PyEval_EvalFrameEx_slp(struct _frame *, int, PyObject *); /* eval_frame with stack overflow, triggered there with a macro */ PyObject * slp_eval_frame_newstack(struct _frame *f, int throwflag, PyObject *retval); -/* other eval_frame functions from module/scheduling.c */ -PyObject * slp_restore_tracing(PyCFrameObject *cf, int exc, PyObject *retval); /* other eval_frame functions from Objects/typeobject.c */ PyObject * slp_tp_init_callback(PyCFrameObject *cf, int exc, PyObject *retval); /* functions related to pickling */ @@ -754,7 +752,6 @@ int slp_prepare_slots(PyTypeObject*); Py_tracefunc slp_get_sys_profile_func(void); Py_tracefunc slp_get_sys_trace_func(void); int slp_encode_ctrace_functions(Py_tracefunc c_tracefunc, Py_tracefunc c_profilefunc); -PyTaskletTStateStruc * slp_get_saved_tstate(PyTaskletObject *task); /* * Channel related prototypes diff --git a/Lib/stackless.py b/Lib/stackless.py index 3b38579fa91a0df..d1c345ec1e7b719 100644 --- a/Lib/stackless.py +++ b/Lib/stackless.py @@ -92,7 +92,7 @@ def _tasklet_get_unpicklable_state(tasklet): flags = pickle_flags(PICKLEFLAGS_PICKLE_CONTEXT, PICKLEFLAGS_PICKLE_CONTEXT) try: - return {'context': tasklet.__reduce__()[2][8]} + return {'context': tasklet.__reduce__()[2][12]} finally: pickle_flags(flags, PICKLEFLAGS_PICKLE_CONTEXT) diff --git a/Stackless/changelog.txt b/Stackless/changelog.txt index 2a2ded3d7bd1475..0e4d74d2b4cf163 100644 --- a/Stackless/changelog.txt +++ b/Stackless/changelog.txt @@ -9,6 +9,11 @@ What's New in Stackless 3.X.X? *Release date: 20XX-XX-XX* +- https://github.com/stackless-dev/stackless/issues/281 + Per tasklet profiling and tracing state information is now stored in the + tasklet itself. This increases the size of tasklet objects a bit, but saves a + lot of code. + - https://github.com/stackless-dev/stackless/issues/284 Stackless supports pickling functions. But previous versions of Stackless only pickled the following attributes of a function-object: '__code__', diff --git a/Stackless/module/scheduling.c b/Stackless/module/scheduling.c index 4e466872bfa07aa..d93045680582da4 100644 --- a/Stackless/module/scheduling.c +++ b/Stackless/module/scheduling.c @@ -322,118 +322,6 @@ slp_init_bombtype(void) return PyType_Ready(&PyBomb_Type); } -/******************************************************************* - - Exception handling revised. - - Every tasklet has its own exception state. The thread state - exists only once for the current thread, so we need to simulate - ownership. - - Whenever a tasklet is run for the first time, it should clear - the exception variables and start with a clean state. - - When it dies normally, it should clean up these. - - When a transfer occours from one tasklet to another, - the first tasklet should save its exception in local variables, - and the other one should restore its own. - - When a tasklet dies with an uncaught exception, this will - be passed on to the main tasklet. The main tasklet must - clear its exception state, if any, and take over those of - the tasklet resting in peace. - - 2002-08-08 This was a misconception. This is not the current - exception, but the one which ceval provides for exception - handlers. That means, sharing of this variable is totally - unrelated to the current error, and we always swap the data. - - Added support for trace/profile as well. - - ********************************************************************/ - - -typedef struct { - intptr_t magic1; - PyTaskletTStateStruc s; - intptr_t magic2; -} saved_tstat_with_magic_t; - -/* not a valid ptr and not a common integer */ -#define SAVED_TSTATE_MAGIC1 (((intptr_t)transfer_with_tracing)+1) -#define SAVED_TSTATE_MAGIC2 (-1*((intptr_t)transfer_with_tracing)) - -static int -transfer_with_tracing(PyCStackObject **cstprev, PyCStackObject *cst, PyTaskletObject *prev) -{ - PyThreadState *ts = _PyThreadState_GET(); - int ret; - - saved_tstat_with_magic_t sm; - sm.magic1 = SAVED_TSTATE_MAGIC1; - sm.magic2 = SAVED_TSTATE_MAGIC2; - - /* prevent overly compiler optimisation. - We store the address of sm into a global variable. - This way the optimizer can't change the layout of the structure. */ - SLP_DO_NOT_OPTIMIZE_AWAY(&sm); - - sm.s.tracing = ts->tracing; - sm.s.c_profilefunc = ts->c_profilefunc; - sm.s.c_tracefunc = ts->c_tracefunc; - sm.s.c_profileobj = ts->c_profileobj; - sm.s.c_traceobj = ts->c_traceobj; - Py_XINCREF(sm.s.c_profileobj); - Py_XINCREF(sm.s.c_traceobj); - - PyEval_SetTrace(NULL, NULL); - PyEval_SetProfile(NULL, NULL); - ts->tracing = 0; - - ret = slp_transfer(cstprev, cst, prev); - - ts->tracing = sm.s.tracing; - PyEval_SetTrace(sm.s.c_tracefunc, sm.s.c_traceobj); - PyEval_SetProfile(sm.s.c_profilefunc, sm.s.c_profileobj); - - sm.magic1 = 0; - sm.magic2 = 0; - Py_XDECREF(sm.s.c_profileobj); - Py_XDECREF(sm.s.c_traceobj); - - return ret; -} - -PyTaskletTStateStruc * -slp_get_saved_tstate(PyTaskletObject *task) { - /* typical offset of the structure from cstate->stack - * is about 20. Therefore 100 should be enough */ - static const Py_ssize_t max_search_size = 100; - PyCStackObject *cst = task->cstate; - Py_ssize_t *p, *p_max; - - if (cst->tstate && cst->tstate->st.current == task) - /* task is current */ - return NULL; - if (cst->task != task) - return NULL; - assert(Py_SIZE(cst) >= 0); - if (Py_SIZE(cst) > max_search_size) - p_max = &(cst->stack[max_search_size]); - else - p_max = &(cst->stack[Py_SIZE(cst)]); - for (p=cst->stack; p!=p_max; p++) { - if (SAVED_TSTATE_MAGIC1 == *p) { - saved_tstat_with_magic_t *sm = (saved_tstat_with_magic_t *)p; - assert(sm->magic1 == SAVED_TSTATE_MAGIC1); - if (sm->magic2 == SAVED_TSTATE_MAGIC2) - /* got it */ - return &(sm->s); - } - } - return NULL; -} /* scheduler monitoring */ @@ -556,33 +444,6 @@ kill_wrap_bad_guy(PyTaskletObject *prev, PyTaskletObject *bad_guy) /* non-recursive scheduling */ -PyObject * -slp_restore_tracing(PyCFrameObject *cf, int exc, PyObject *retval) -{ - PyThreadState *ts = _PyThreadState_GET(); - - if (NULL == cf->any1 && NULL == cf->any2) { - /* frame was created by unpickling */ - if (cf->n & 1) { - Py_tracefunc func = slp_get_sys_trace_func(); - if (NULL == func) - return NULL; - cf->any1 = func; - } - if (cf->n & 2) { - Py_tracefunc func = slp_get_sys_profile_func(); - if (NULL == func) - return NULL; - cf->any2 = func; - } - } - ts->tracing = cf->i; - PyEval_SetTrace((Py_tracefunc)cf->any1, cf->ob1); - PyEval_SetProfile((Py_tracefunc)cf->any2, cf->ob2); - SLP_STORE_NEXT_FRAME(ts, cf->f_back); - return STACKLESS_PACK(ts, retval); -} - int slp_encode_ctrace_functions(Py_tracefunc c_tracefunc, Py_tracefunc c_profilefunc) { @@ -771,6 +632,54 @@ Py_LOCAL_INLINE(void) SLP_UPDATE_TSTATE_ON_SWITCH(PyThreadState *tstate, PyTaskl tstate->context = next->context; tstate->context_ver++; next->context = NULL; + /* And now the same for the trace and profile state: + * - save the state form tstate to prev + * - move the state from next to tstate + */ + assert(prev->profilefunc == NULL); + assert(prev->tracefunc == NULL); + assert(prev->profileobj == NULL); + assert(prev->traceobj == NULL); + assert(prev->tracing == 0); + if (tstate->c_profilefunc || next->profilefunc) { + prev->profilefunc = tstate->c_profilefunc; + prev->profileobj = tstate->c_profileobj; + Py_XINCREF(prev->profileobj); + if (prev->profileobj) + assert(Py_REFCNT(prev->profileobj) >= 2); /* won't drop to zero in PyEval_SetProfile */ + PyEval_SetProfile(next->profilefunc, next->profileobj); + next->profilefunc = NULL; + if (next->profileobj) + assert(Py_REFCNT(next->profileobj) >= 2); /* won't drop to zero */ + Py_CLEAR(next->profileobj); + } else { + /* If you use a Python profileobj, profilefunc is sysmodule.c profile_trampoline(). + * Therefore, if profilefunc is NULL, profileobj must be NULL too. + */ + assert(tstate->c_profileobj == NULL); + assert(next->profileobj == NULL); + } + if (tstate->c_tracefunc || next->tracefunc) { + prev->tracefunc = tstate->c_tracefunc; + prev->traceobj = tstate->c_traceobj; + Py_XINCREF(prev->traceobj); + if (prev->traceobj) + assert(Py_REFCNT(prev->traceobj) >= 2); /* won't drop to zero in PyEval_SetTrace */ + prev->tracing = tstate->tracing; + tstate->tracing = next->tracing; + PyEval_SetTrace(next->tracefunc, next->traceobj); + next->tracefunc = NULL; + if (next->traceobj) + assert(Py_REFCNT(next->traceobj) >= 2); /* won't drop to zero */ + Py_CLEAR(next->traceobj); + next->tracing = 0; + } else { + /* If you use a Python traceobj, tracefunc is sysmodule.c trace_trampoline(). + * Therefore, if tracefunc is NULL, traceobj must be NULL too. + */ + assert(tstate->c_traceobj == NULL); + assert(next->traceobj == NULL); + } } #else #define SLP_UPDATE_TSTATE_ON_SWITCH(tstate__, prev_, next_) \ @@ -784,6 +693,51 @@ Py_LOCAL_INLINE(void) SLP_UPDATE_TSTATE_ON_SWITCH(PyThreadState *tstate, PyTaskl ts__->context = next__->context; \ ts__->context_ver++; \ next__->context = NULL; \ + /* And now the same for the trace and profile state: */ \ + /* - save the state form tstate to prev */ \ + /* - move the state from next to tstate */ \ + assert(prev__->profilefunc == NULL); \ + assert(prev__->tracefunc == NULL); \ + assert(prev__->profileobj == NULL); \ + assert(prev__->traceobj == NULL); \ + assert(prev__->tracing == 0); \ + if (ts__->c_profilefunc || next__->profilefunc) { \ + prev__->profilefunc = ts__->c_profilefunc; \ + prev__->profileobj = ts__->c_profileobj; \ + Py_XINCREF(prev__->profileobj); \ + if (prev__->profileobj) \ + assert(Py_REFCNT(prev__->profileobj) >= 2); /* won't drop to zero in PyEval_SetProfile */ \ + PyEval_SetProfile(next__->profilefunc, next__->profileobj); \ + next__->profilefunc = NULL; \ + if (next__->profileobj) \ + assert(Py_REFCNT(next__->profileobj) >= 2); /* won't drop to zero */ \ + Py_CLEAR(next__->profileobj); \ + } else { \ + /* If you use a Python profileobj, profilefunc is sysmodule.c profile_trampoline(). */ \ + /* Therefore, if profilefunc is NULL, profileobj must be NULL too. */ \ + assert(ts__->c_profileobj == NULL); \ + assert(next__->profileobj == NULL); \ + } \ + if (ts__->c_tracefunc || next__->tracefunc) { \ + prev__->tracefunc = ts__->c_tracefunc; \ + prev__->traceobj = ts__->c_traceobj; \ + Py_XINCREF(prev__->traceobj); \ + if (prev__->traceobj) \ + assert(Py_REFCNT(prev__->traceobj) >= 2); /* won't drop to zero in PyEval_SetTrace */ \ + prev__->tracing = ts__->tracing; \ + ts__->tracing = next__->tracing; \ + PyEval_SetTrace(next__->tracefunc, next__->traceobj); \ + next__->tracefunc = NULL; \ + if (next__->traceobj) \ + assert(Py_REFCNT(next__->traceobj) >= 2); /* won't drop to zero */ \ + Py_CLEAR(next__->traceobj); \ + next__->tracing = 0; \ + } else { \ + /* If you use a Python traceobj, tracefunc is sysmodule.c trace_trampoline(). */ \ + /* Therefore, if tracefunc is NULL, traceobj must be NULL too. */ \ + assert(ts__->c_traceobj == NULL); \ + assert(next__->traceobj == NULL); \ + } \ } while(0) #endif @@ -1078,7 +1032,6 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject { PyCStackObject **cstprev; PyObject *retval; - int (*transfer)(PyCStackObject **, PyCStackObject *, PyTaskletObject *); int transfer_result; /* remove the no-soft-irq flag from the runflags */ @@ -1121,31 +1074,6 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject return -1; } - if (ts->use_tracing || ts->tracing) { - /* build a shadow frame if we are returning here */ - if (prev->f.frame != NULL) { - PyCFrameObject *f = slp_cframe_new(slp_restore_tracing, 0); - int c_functions = slp_encode_ctrace_functions(ts->c_tracefunc, ts->c_profilefunc); - if (f == NULL || c_functions == -1) - return -1; - f->any1 = ts->c_tracefunc; - f->any2 = ts->c_profilefunc; - f->i = ts->tracing; - f->n = c_functions; - assert(NULL == f->ob1); - assert(NULL == f->ob2); - f->ob1 = ts->c_traceobj; - f->ob2 = ts->c_profileobj; - Py_XINCREF(f->ob1); - Py_XINCREF(f->ob2); - assert(f->f_back == NULL); - f->f_back = prev->f.frame; /* steal the reference */ - prev->f.frame = (PyFrameObject *) f; - } - PyEval_SetTrace(NULL, NULL); - PyEval_SetProfile(NULL, NULL); - ts->tracing = 0; - } assert(next->cstate != NULL); if (next->cstate->nesting_level != 0) { @@ -1213,14 +1141,10 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject Py_CLEAR(next->f.frame); ++ts->st.nesting_level; - if (ts->use_tracing || ts->tracing) - transfer = transfer_with_tracing; - else - transfer = slp_transfer; SLP_UPDATE_TSTATE_ON_SWITCH(ts, prev, next); - transfer_result = transfer(cstprev, next->cstate, prev); + transfer_result = slp_transfer(cstprev, next->cstate, prev); /* Note: If the transfer was successful from here on "prev" holds the * currently executing tasklet and "next" is the previous tasklet. */ @@ -1237,29 +1161,6 @@ slp_schedule_task_prepared(PyThreadState *ts, PyObject **result, PyTaskletObject *did_switch = 1; *result = retval; - /* Now evaluate any pending (slp_restore_tracing) cframes. - * They were inserted by tasklet_set_trace_function or - * tasklet_set_profile_function. We must process them here, because the - * restored cstack doesn't contain them. - * (For now there is at most a single pending cframe.) - */ - if (prev->cstate->nesting_level > 0 && f && PyCFrame_Check(f) && - ((PyCFrameObject *)f)->f_execute == slp_restore_tracing) { - PyObject *retval; - PyFrameObject *f2; - - /* the next frame must be a real frame */ - assert(f->f_back && PyFrame_Check(f->f_back)); - - retval = CALL_FRAME_FUNCTION(f, 0, Py_None); - STACKLESS_UNPACK(ts, retval); - assert(retval == Py_None); - f2 = SLP_CLAIM_NEXT_FRAME(ts); - assert(f2 == f->f_back); - Py_DECREF(f); - f = f2; - assert(PyFrame_Check(SLP_CURRENT_FRAME(ts))); - } assert(f == NULL || Py_REFCNT(f) >= 2); Py_XDECREF(f); return 0; @@ -1389,8 +1290,14 @@ schedule_task_destruct(PyObject **retval, PyTaskletObject *prev, PyTaskletObject } fail = slp_schedule_task(retval, prev, next, 1, &switched); /* it should either fail or switch */ - if (!fail) + if (!fail) { assert(switched); + /* clear tracing and profiling state for compatibility with Stackless versions < 3.8 */ + prev->profilefunc = prev->tracefunc = NULL; + prev->tracing = 0; + Py_CLEAR(prev->profileobj); + Py_CLEAR(prev->traceobj); + } if (fail) { /* something happened, cancel our decref manipulations. */ if (tempval != NULL) { diff --git a/Stackless/module/taskletobject.c b/Stackless/module/taskletobject.c index 20bf77b54c94438..f24089ea0deeaa9 100644 --- a/Stackless/module/taskletobject.c +++ b/Stackless/module/taskletobject.c @@ -172,6 +172,8 @@ tasklet_traverse(PyTaskletObject *t, visitproc visit, void *arg) Py_VISIT(t->exc_state.exc_value); Py_VISIT(t->exc_state.exc_traceback); Py_VISIT(t->context); + Py_VISIT(t->profileobj); + Py_VISIT(t->traceobj); return 0; } @@ -204,6 +206,10 @@ tasklet_clear(PyTaskletObject *t) Py_CLEAR(t->tempval); Py_CLEAR(t->def_globals); Py_CLEAR(t->context); + t->profilefunc = t->tracefunc = NULL; + t->tracing = 0; + Py_CLEAR(t->profileobj); + Py_CLEAR(t->traceobj); /* unlink task from cstate */ if (t->cstate != NULL && t->cstate->task == t) @@ -530,7 +536,7 @@ tasklet_bind(PyObject *self, PyObject *args, PyObject *kwargs) return self; } -#define TASKLET_TUPLEFMT "iOiOOOOO" +#define TASKLET_TUPLEFMT "iOiOOOOOiiOO" static PyObject * tasklet_new(PyTypeObject *type, PyObject *args, PyObject *kwds) @@ -620,6 +626,8 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value) PyThreadState *ts = t->cstate->tstate; PyObject *exc_type, *exc_value, *exc_traceback, *exc_info; PyObject *context = NULL; + int tracing, c_functions; + PyObject *profileobj, *traceobj; if (value && !PyLong_Check(value)) { PyErr_SetString(PyExc_TypeError, "__reduce_ex__ argument should be an integer"); @@ -633,20 +641,14 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value) if (lis == NULL) goto err_exit; f = t->f.frame; while (f != NULL) { - int append_frame = 1; - if (PyCFrame_Check(f) && ((PyCFrameObject *)f)->f_execute == slp_restore_tracing) { - append_frame = ts->st.pickleflags & SLP_PICKLEFLAGS_PRESERVE_TRACING_STATE; - } - if (append_frame) { - int ret; - PyObject * frame_reducer = slp_reduce_frame(f); - if (frame_reducer == NULL) - goto err_exit; - ret = PyList_Append(lis, frame_reducer); - Py_DECREF(frame_reducer); - if (ret) - goto err_exit; - } + int ret; + PyObject * frame_reducer = slp_reduce_frame(f); + if (frame_reducer == NULL) + goto err_exit; + ret = PyList_Append(lis, frame_reducer); + Py_DECREF(frame_reducer); + if (ret) + goto err_exit; f = f->f_back; } if (PyList_Reverse(lis)) goto err_exit; @@ -661,6 +663,24 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value) if (NULL == context) goto err_exit; + if (ts && ts->st.pickleflags & SLP_PICKLEFLAGS_PRESERVE_TRACING_STATE) { + c_functions = slp_encode_ctrace_functions(t->tracefunc, t->profilefunc); + if (-1 == c_functions) + goto err_exit; + tracing = t->tracing; + profileobj = t->profileobj; + if (NULL == profileobj) + profileobj = Py_None; + traceobj = t->traceobj; + if (NULL == traceobj) + traceobj = Py_None; + } else { + c_functions = 0; + tracing = 0; + profileobj = Py_None; + traceobj = Py_None; + } + assert(!ts || t->exc_info != &ts->exc_state); /* Because of the test a few lines above, it is guaranteed that t is not the current tasklet. * Therefore we can simplify the line @@ -700,6 +720,10 @@ tasklet_reduce(PyTaskletObject * t, PyObject *value) exc_value, exc_traceback, exc_info, + tracing, + c_functions, + profileobj, + traceobj, context ); Py_DECREF(exc_info); @@ -730,6 +754,8 @@ tasklet_setstate(PyObject *self, PyObject *args) PyObject *exc_type, *exc_value, *exc_traceback; PyObject *old_type, *old_value, *old_traceback; PyObject *exc_info_obj; + int tracing, c_functions; + PyObject *profileobj, *traceobj; PyObject *context = NULL; PyFrameObject *f; Py_ssize_t i, nframes; @@ -740,7 +766,7 @@ tasklet_setstate(PyObject *self, PyObject *args) if (PyTasklet_Alive(t)) RUNTIME_ERROR("tasklet is alive", NULL); - if (!PyArg_ParseTuple(args, "iOiO!OOOO|O:tasklet", + if (!PyArg_ParseTuple(args, "iOiO!OOOOiiOO|O:tasklet", &flags, &tempval, &nesting_level, @@ -749,6 +775,10 @@ tasklet_setstate(PyObject *self, PyObject *args) &exc_value, &exc_traceback, &exc_info_obj, + &tracing, + &c_functions, + &profileobj, + &traceobj, &context)) return NULL; @@ -808,6 +838,39 @@ tasklet_setstate(PyObject *self, PyObject *args) if(NULL == context && _tasklet_init_context(t)) return NULL; } + + /* profile and tracing */ + if (c_functions & 1) { + Py_tracefunc func = slp_get_sys_trace_func(); + if (NULL == func) + return NULL; + t->tracefunc = func; + } else { + t->tracefunc = NULL; + } + if (c_functions & 2) { + Py_tracefunc func = slp_get_sys_profile_func(); + if (NULL == func) + return NULL; + t->profilefunc = func; + } else { + t->profilefunc = NULL; + } + if (Py_None != profileobj) { + Py_INCREF(profileobj); + Py_XSETREF(t->profileobj, profileobj); + } else { + Py_CLEAR(t->profileobj); + } + if (Py_None != traceobj) { + Py_INCREF(traceobj); + Py_XSETREF(t->traceobj, traceobj); + } else { + Py_CLEAR(t->traceobj); + } + t->tracing = tracing; + + /* context */ if (context) { PyObject *obj = _stackless_tasklet_set_context_impl(t, context); if (NULL == obj) @@ -2050,53 +2113,20 @@ static PyObject * tasklet_get_trace_function(PyTaskletObject *task, void *closure) { PyThreadState *ts = task->cstate->tstate; - PyCFrameObject *f; - PyTaskletTStateStruc *saved_ts; + PyObject *retval; - if (ts && ts->st.current == task) { - /* current tasklet */ - PyObject *temp = ts->c_traceobj; - if (temp == NULL) - temp = Py_None; - Py_INCREF(temp); - return temp; - } - - f = task->f.cframe; - while (NULL != f && PyCFrame_Check(f)) { - if (f->f_execute == slp_restore_tracing) { - /* we found a restore tracing frame */ - PyObject *temp = f->ob1; - if (temp == NULL) - temp = Py_None; - Py_INCREF(temp); - return temp; - } - f = (PyCFrameObject *)(f->f_back); - } - - /* try the saved tstate of a hard switched tasklet */ - saved_ts = slp_get_saved_tstate(task); - if (NULL != saved_ts) { - PyObject *temp; - temp = saved_ts->c_traceobj; - if (NULL == temp) - temp = Py_None; - Py_INCREF(temp); - return temp; - } - - Py_INCREF(Py_None); - return Py_None; + retval = (ts && ts->st.current == task) ? ts->c_traceobj : task->traceobj; + if (retval == NULL) + retval = Py_None; + Py_INCREF(retval); + return retval; } static int tasklet_set_trace_function(PyTaskletObject *task, PyObject *value, void *closure) { PyThreadState *ts = task->cstate->tstate; - PyCFrameObject *f; Py_tracefunc tf = NULL; - PyTaskletTStateStruc *saved_ts; if (Py_None == value) value = NULL; @@ -2113,124 +2143,31 @@ tasklet_set_trace_function(PyTaskletObject *task, PyObject *value, void *closure return 0; } - f = task->f.cframe; - while (NULL != f && PyCFrame_Check(f)) { - if (f->f_execute == slp_restore_tracing) { - /* we found a restore tracing frame */ - PyObject *temp; - int c_functions = slp_encode_ctrace_functions(tf, f->any2); - if (c_functions < 0) - return -1; - temp = f->ob1; - Py_XINCREF(value); - f->ob1 = value; - f->any1 = tf; - f->n = c_functions; - Py_XDECREF(temp); - - return 0; - } - f = (PyCFrameObject *)(f->f_back); - } - - /* try the saved tstate of a hard switched tasklet */ - saved_ts = slp_get_saved_tstate(task); - if (NULL != saved_ts) { - PyObject *temp; - temp = saved_ts->c_traceobj; - Py_XINCREF(value); - saved_ts->c_traceobj = value; - saved_ts->c_tracefunc = tf; - Py_XDECREF(temp); - - return 0; - } - - /* task is neither current nor has a restore_tracing frame. - ==> tracing is currently off */ - if (NULL == value) - /* nothing to do */ - return 0; - - /* here we must add a restore tracing cframe */ - f = task->f.cframe; - if (NULL != f) { - /* Insert a new cframe */ - PyCFrameObject *cf; - int c_functions = slp_encode_ctrace_functions(tf, f->any2); - if (c_functions < 0) - return -1; - cf = slp_cframe_new(slp_restore_tracing, 0); - if (cf == NULL) - return -1; - Py_INCREF(f); - cf->f_back = (PyFrameObject *)f; - Py_INCREF(value); - assert(NULL == cf->ob1); - cf->ob1 = value; - cf->any1 = tf; - cf->n = c_functions; - assert(0 == cf->i); /* ts->tracing */ - assert(NULL == cf->any2); /* ts->c_profilefunc */ - assert(NULL == cf->ob2); /* ts->c_profileobj */ - Py_SETREF(task->f.cframe, cf); - return 0; - } - - RUNTIME_ERROR("tasklet is not alive", -1); + /* tasklet is not current */ + task->tracefunc = tf; + Py_XINCREF(value); + Py_XSETREF(task->traceobj, value); + return 0; } static PyObject * tasklet_get_profile_function(PyTaskletObject *task, void *closure) { PyThreadState *ts = task->cstate->tstate; - PyCFrameObject *f; - PyTaskletTStateStruc *saved_ts; - - if (ts && ts->st.current == task) { - /* current tasklet */ - PyObject *temp = ts->c_profileobj; - if (temp == NULL) - temp = Py_None; - Py_INCREF(temp); - return temp; - } - - f = task->f.cframe; - while (NULL != f && PyCFrame_Check(f)) { - if (f->f_execute == slp_restore_tracing) { - /* we found a restore tracing frame */ - PyObject *temp = f->ob2; - if (temp == NULL) - temp = Py_None; - Py_INCREF(temp); - return temp; - } - f = (PyCFrameObject *)(f->f_back); - } - - /* try the saved tstate of a hard switched tasklet */ - saved_ts = slp_get_saved_tstate(task); - if (NULL != saved_ts) { - PyObject *temp; - temp = saved_ts->c_profileobj; - if (NULL == temp) - temp = Py_None; - Py_INCREF(temp); - return temp; - } + PyObject *retval; - Py_INCREF(Py_None); - return Py_None; + retval = (ts && ts->st.current == task) ? ts->c_profileobj : task->profileobj; + if (retval == NULL) + retval = Py_None; + Py_INCREF(retval); + return retval; } static int tasklet_set_profile_function(PyTaskletObject *task, PyObject *value, void *closure) { PyThreadState *ts = task->cstate->tstate; - PyCFrameObject *f; Py_tracefunc tf = NULL; - PyTaskletTStateStruc *saved_ts; if (Py_None == value) value = NULL; @@ -2247,71 +2184,11 @@ tasklet_set_profile_function(PyTaskletObject *task, PyObject *value, void *closu return 0; } - f = task->f.cframe; - while (NULL != f && PyCFrame_Check(f)) { - if (f->f_execute == slp_restore_tracing) { - PyObject *temp; - int c_functions = slp_encode_ctrace_functions(f->any1, tf); - if (c_functions < 0) - return -1; - /* we found a restore tracing frame */ - temp = f->ob2; - Py_XINCREF(value); - f->ob2 = value; - f->any2 = tf; - f->n = c_functions; - Py_XDECREF(temp); - - return 0; - } - f = (PyCFrameObject *)(f->f_back); - } - - /* try the saved tstate of a hard switched tasklet */ - saved_ts = slp_get_saved_tstate(task); - if (NULL != saved_ts) { - PyObject *temp; - temp = saved_ts->c_profileobj; - Py_XINCREF(value); - saved_ts->c_profileobj = value; - saved_ts->c_profilefunc = tf; - Py_XDECREF(temp); - - return 0; - } - - /* task is neither current nor has a restore_tracing frame. - ==> tracing is currently off */ - if (NULL == value) - /* nothing to do */ - return 0; - - /* here we must add an restore tracing cframe */ - f = task->f.cframe; - if (NULL != f) { - /* Insert a new cframe */ - PyCFrameObject *cf; - int c_functions = slp_encode_ctrace_functions(f->any1, tf); - if (c_functions < 0) - return -1; - cf = slp_cframe_new(slp_restore_tracing, 0); - if (cf == NULL) - return -1; - Py_INCREF(f); - cf->f_back = (PyFrameObject *)f; - Py_INCREF(value); - assert(NULL == cf->ob2); - cf->ob2 = value; - cf->any2 = tf; - cf->n = c_functions; - assert(0 == cf->i); /* ts->tracing */ - assert(NULL == cf->any1); /* ts->c_tracefunc */ - assert(NULL == cf->ob1); /* ts->c_traceobj */ - Py_SETREF(task->f.cframe, cf); - return 0; - } - - RUNTIME_ERROR("tasklet is not alive", -1); + /* tasklet is not current */ + task->profilefunc = tf; + Py_XINCREF(value); + Py_XSETREF(task->profileobj, value); + return 0; } /*[clinic input] diff --git a/Stackless/pickling/prickelpit.c b/Stackless/pickling/prickelpit.c index 090820a015f1e7f..f52633b82a7d64f 100644 --- a/Stackless/pickling/prickelpit.c +++ b/Stackless/pickling/prickelpit.c @@ -986,7 +986,6 @@ static int init_functype(PyObject * mod) #define frametuplefmt "O)(OibOiOOiiOO" SLP_DEF_INVALID_EXEC(slp_channel_seq_callback) -SLP_DEF_INVALID_EXEC(slp_restore_tracing) SLP_DEF_INVALID_EXEC(slp_tp_init_callback) static PyTypeObject wrap_PyFrame_Type; @@ -1328,8 +1327,6 @@ static int init_frametype(PyObject * mod) { return slp_register_execute(&PyCFrame_Type, "channel_seq_callback", slp_channel_seq_callback, SLP_REF_INVALID_EXEC(slp_channel_seq_callback)) - || slp_register_execute(&PyCFrame_Type, "slp_restore_tracing", - slp_restore_tracing, SLP_REF_INVALID_EXEC(slp_restore_tracing)) || slp_register_execute(&PyCFrame_Type, "slp_tp_init_callback", slp_tp_init_callback, SLP_REF_INVALID_EXEC(slp_tp_init_callback)) || init_type(&wrap_PyFrame_Type, initchain, mod); diff --git a/Stackless/unittests/test_exception.py b/Stackless/unittests/test_exception.py index a7e7132d8f4573a..cdd4c3a188fb522 100644 --- a/Stackless/unittests/test_exception.py +++ b/Stackless/unittests/test_exception.py @@ -27,8 +27,8 @@ def test_tasklet_reduce_wo_exc(self): self.assertEqual(len(state), 3) state = state[2] self.assertIsInstance(state, tuple) - self.assertEqual(len(state), 8) - self.assertTupleEqual(state[-4:], (None, None, None, None)) + self.assertEqual(len(state), 12) + self.assertTupleEqual(state[4:8], (None, None, None, None)) def test_tasklet_pickle_with_exc(self): self.skipUnlessSoftswitching() diff --git a/Stackless/unittests/test_tracing.py b/Stackless/unittests/test_tracing.py index 5518286887f5412..2e7ad9eeae3a0b6 100644 --- a/Stackless/unittests/test_tracing.py +++ b/Stackless/unittests/test_tracing.py @@ -129,14 +129,14 @@ def testSetTraceOnCurrent(self): def testSetTraceOnDeadTasklet(self): t = stackless.tasklet() self.assertIsNone(t.trace_function) - self.assertRaisesRegex(RuntimeError, "tasklet is not alive", - setattr, t, "trace_function", self.nullTraceFunc) + t.trace_function = self.nullTraceFunc + self.assertIs(t.trace_function.__func__, self.nullTraceFunc.__func__) def testSetProfileOnDeadTasklet(self): t = stackless.tasklet() self.assertIsNone(t.trace_function) - self.assertRaisesRegex(RuntimeError, "tasklet is not alive", - setattr, t, "profile_function", self.nullTraceFunc) + t.profile_function = self.nullTraceFunc + self.assertIs(t.profile_function.__func__, self.nullTraceFunc.__func__) def testSetTraceOnTasklet1(self): # Change trace state of a non-current tasklet diff --git a/Stackless/unittests/test_tstate.py b/Stackless/unittests/test_tstate.py index b1bca3f08c998b5..483677f4e1decf7 100644 --- a/Stackless/unittests/test_tstate.py +++ b/Stackless/unittests/test_tstate.py @@ -332,16 +332,19 @@ def testReduceOfTracingState(self): return # test if the tracing cframe is present / not present - reduce_frame = get_reduce_frame() - self.assertListEqual(reduced_tasklet1[2][3], [reduce_frame(frame)]) - self.assertEqual(len(reduced_tasklet2[2][3]), 2) - self.assertIs(reduced_tasklet2[2][3][0], reduce_frame(frame)) - self.assertIsInstance(reduced_tasklet2[2][3][1], stackless.cframe) - - cf = reduced_tasklet2[2][3][1] - reduced_cframe = cf.__reduce__() - self.assertEqual(reduced_cframe[2][1], 'slp_restore_tracing') - self.assertIs(reduced_cframe[2][2][1].__func__, self.Callback.__func__) + self.assertIsInstance(reduced_tasklet1[2][8], int) + self.assertEqual(reduced_tasklet1[2][8], 0) + self.assertIsInstance(reduced_tasklet1[2][9], int) + self.assertEqual(reduced_tasklet1[2][9], 0) + self.assertIsNone(reduced_tasklet1[2][10]) + self.assertIsNone(reduced_tasklet1[2][11]) + + self.assertIsInstance(reduced_tasklet2[2][8], int) + self.assertEqual(reduced_tasklet2[2][8], 0) + self.assertIsInstance(reduced_tasklet2[2][9], int) + self.assertEqual(reduced_tasklet2[2][9], 1) + self.assertIsNone(reduced_tasklet2[2][10]) + self.assertIs(reduced_tasklet2[2][11].__func__, self.Callback.__func__) if __name__ == '__main__': if not sys.argv[1:]: