static int bind_tasklet_to_frame(PyTaskletObject *task, PyFrameObject *frame) { PyThreadState *ts = task->cstate->tstate; 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; } return 0; /* note: We expect that f_back is NULL, or will be adjusted immediately */ }
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; assert(PyType_IsSubtype(type, &PyTasklet_Type)); t = (PyTaskletObject *) type->tp_alloc(type, 0); if (t != NULL) { *(int*)&t->flags = 0; t->next = NULL; t->prev = NULL; t->f.frame = NULL; if (func == NULL) func = Py_None; Py_INCREF(func); t->tempval = func; t->tsk_weakreflist = NULL; Py_INCREF(ts->st.initial_stub); t->cstate = ts->st.initial_stub; 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; }
PyObject * slp_schedule_task(PyTaskletObject *prev, PyTaskletObject *next, int stackless) { PyThreadState *ts = PyThreadState_GET(); PyCStackObject **cstprev; PyObject *retval; int (*transfer)(PyCStackObject **, PyCStackObject *, PyTaskletObject *); if (next == NULL) { return schedule_task_block(prev, stackless); } #ifdef WITH_THREAD /* note that next->cstate is undefined if it is ourself */ if (next->cstate != NULL && next->cstate->tstate != ts) { return schedule_task_unblock(prev, next, stackless); } #endif if (next->flags.blocked) { /* unblock from channel */ slp_channel_remove_slow(next); slp_current_insert(next); } else if (next->next == NULL) { /* reactivate floating task */ Py_INCREF(next); slp_current_insert(next); } if (prev == next) { retval = prev->tempval; Py_INCREF(retval); if (PyBomb_Check(retval)) retval = slp_bomb_explode(prev); return retval; } NOTIFY_SCHEDULE(prev, next, NULL); ts->st.ticker = ts->st.interval; prev->recursion_depth = ts->recursion_depth; prev->f.frame = ts->frame; if (!stackless || ts->st.nesting_level != 0) goto hard_switching; /* start of soft switching code */ 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 NULL; } /* handle exception */ if (ts->exc_type == Py_None) { Py_XDECREF(ts->exc_type); ts->exc_type = NULL; } if (ts->exc_type != NULL) { /* build a shadow frame */ PyCFrameObject *f = slp_cframe_new(restore_exception, 1); if (f == NULL) return NULL; f->ob1 = ts->exc_type; f->ob2 = ts->exc_value; f->ob3 = ts->exc_traceback; prev->f.frame = (PyFrameObject *) f; ts->exc_type = ts->exc_value = ts->exc_traceback = NULL; } if (ts->use_tracing || ts->tracing) { /* build a shadow frame */ PyCFrameObject *f = slp_cframe_new(restore_tracing, 1); if (f == NULL) return NULL; f->any1 = ts->c_tracefunc; f->any2 = ts->c_profilefunc; ts->c_tracefunc = ts->c_profilefunc = NULL; f->ob1 = ts->c_traceobj; f->ob2 = ts->c_profileobj; /* trace/profile does not add references */ Py_XINCREF(f->ob1); Py_XINCREF(f->ob2); ts->c_traceobj = ts->c_profileobj = NULL; f->i = ts->tracing; f->n = ts->use_tracing; ts->tracing = ts->use_tracing = 0; prev->f.frame = (PyFrameObject *) f; } ts->frame = next->f.frame; next->f.frame = NULL; ts->recursion_depth = next->recursion_depth; ts->st.current = next; retval = next->tempval; assert(next->cstate != NULL); if (next->cstate->nesting_level != 0) { /* create a helper frame to restore the target stack */ ts->frame = (PyFrameObject *) slp_cframe_new(jump_soft_to_hard, 1); if (ts->frame == NULL) { ts->frame = prev->f.frame; return NULL; } /* note that we don't explode the bomb now and don't incref! */ return STACKLESS_PACK(retval); } Py_INCREF(retval); if (PyBomb_Check(retval)) retval = slp_bomb_explode(next); return STACKLESS_PACK(retval); hard_switching: /* 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; ts->st.current = next; if (ts->exc_type == Py_None) { Py_XDECREF(ts->exc_type); ts->exc_type = NULL; } ts->recursion_depth = next->recursion_depth; ts->frame = next->f.frame; next->f.frame = NULL; ++ts->st.nesting_level; if (ts->exc_type != NULL || ts->use_tracing || ts->tracing) transfer = transfer_with_exc; else transfer = slp_transfer; if (transfer(cstprev, next->cstate, prev) == 0) { --ts->st.nesting_level; retval = prev->tempval; Py_INCREF(retval); if (PyBomb_Check(retval)) retval = slp_bomb_explode(prev); return retval; } else { --ts->st.nesting_level; kill_wrap_bad_guy(prev, next); return NULL; } }