static TASKLET_RAISE_EXCEPTION_HEAD(impl_tasklet_raise_exception) { STACKLESS_GETARG(); PyThreadState *ts = PyThreadState_GET(); PyObject *bomb; if (ts->st.main == NULL) return PyTasklet_RaiseException_M(self, klass, args); bomb = slp_make_bomb(klass, args, "tasklet.raise_exception"); if (bomb == NULL) return NULL; TASKLET_SETVAL_OWN(self, bomb); /* if the tasklet is dead, do not run it (no frame) but explode */ if (slp_get_frame(self) == NULL) { TASKLET_CLAIMVAL(self, &bomb); return slp_bomb_explode(bomb); } return slp_schedule_task(ts->st.current, self, stackless, 0); }
static PyObject * generic_channel_action(PyChannelObject *self, PyObject *arg, int dir, int stackless) { PyThreadState *ts = PyThreadState_GET(); 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; PyObject *tmpval, *retval; int fail; assert(abs(dir) == 1); /* set the channel tmpval here, for the callback */ TASKLET_CLAIMVAL(source, &tmpval); TASKLET_SETVAL(source, arg); /* note that notify might release the GIL. */ /* XXX for the moment, we notify late on interthread */ if (!interthread) NOTIFY_CHANNEL(self, source, dir, cando, NULL); if (cando) /* communication 1): there is somebody waiting */ fail = generic_channel_cando(ts, &retval, self, dir, stackless); else fail = generic_channel_block(ts, &retval, self, dir, stackless); if (fail) { TASKLET_SETVAL_OWN(source, tmpval); return NULL; } Py_DECREF(tmpval); if (interthread) NOTIFY_CHANNEL(self, source, dir, cando, NULL); return retval; }
static PyObject * schedule_task_block(PyTaskletObject *prev, int stackless) { PyThreadState *ts = PyThreadState_GET(); PyObject *retval; PyTaskletObject *next = NULL; PyObject *unlocker_lock; if (check_for_deadlock()) { /* revive real main if floating */ if (ts == slp_initial_tstate && ts->st.main->next == NULL) { /* emulate old revive_main behavior: * passing a value only if it is an exception */ if (PyBomb_Check(prev->tempval)) TASKLET_SETVAL(ts->st.main, prev->tempval); return slp_schedule_task(prev, ts->st.main, stackless); } if (!(retval = make_deadlock_bomb())) return NULL; TASKLET_SETVAL_OWN(prev, retval); return slp_schedule_task(prev, prev, stackless); } #ifdef WITH_THREAD if (ts->st.thread.self_lock == NULL) { if (!(ts->st.thread.self_lock = new_lock())) return NULL; acquire_lock(ts->st.thread.self_lock, 1); if (!(ts->st.thread.unlock_lock = new_lock())) return NULL; } /* let somebody reactivate us */ ts->st.thread.is_locked = 1; /* flag as blocked and wait */ PyEval_SaveThread(); PR("locker waiting for my lock"); acquire_lock(ts->st.thread.self_lock, 1); PR("HAVE my lock"); PyEval_RestoreThread(ts); if (temp.unlock_target != NULL) { next = temp.unlock_target; temp.unlock_target = NULL; } else next = prev; /* * get in shape. can't do this with schedule here because * hard switching might not get us back, soon enough. */ 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 (temp.other_lock != NULL) { PR("releasing unlocker"); unlocker_lock = temp.other_lock; temp.other_lock = NULL; release_lock(unlocker_lock); Py_DECREF(unlocker_lock); } ts->st.thread.is_locked = 0; #else (void)unlocker_lock; next = prev; #endif /* this must be after releasing the locks because of hard switching */ retval = slp_schedule_task(prev, next, stackless); PR("schedule() is done"); return retval; }
static PyObject * tasklet_end(PyObject *retval) { PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *task = ts->st.current; PyTaskletObject *next; int ismain = task == ts->st.main; /* * see whether we have a SystemExit, which is no error. * Note that TaskletExit is a subclass. * otherwise make the exception into a bomb. */ if (retval == NULL) { if (PyErr_ExceptionMatches(PyExc_SystemExit)) { /* but if it is truly a SystemExit on the main thread, we want the exit! */ if (ts == slp_initial_tstate && !PyErr_ExceptionMatches(PyExc_TaskletExit)) PyStackless_HandleSystemExit(); PyErr_Clear(); Py_INCREF(Py_None); retval = Py_None; } else { retval = slp_curexc_to_bomb(); if (retval == NULL) return NULL; } } /* * put the result back into the dead tasklet, to give * possible referers access to the return value */ TASKLET_SETVAL_OWN(task, retval); if (ismain) { /* * See whether we need to adjust main's context before * returning */ if (ts->st.serial_last_jump != ts->st.serial) { slp_transfer_return(task->cstate); } } /* remove from runnables */ slp_current_remove(); /* * clean up any current exception - this tasklet is dead. * This only happens if we are killing tasklets in the middle * of their execution. */ if (ts->exc_type != NULL && ts->exc_type != Py_None) { Py_DECREF(ts->exc_type); Py_XDECREF(ts->exc_value); Py_XDECREF(ts->exc_traceback); ts->exc_type = ts->exc_value = ts->exc_traceback = NULL; } /* capture all exceptions */ if (ismain) { /* * Main wants to exit. We clean up, but leave the * runnables chain intact. */ ts->st.main = NULL; Py_DECREF(task); return schedule_task_destruct(task, task); } next = ts->st.current; if (next == NULL) { int blocked = ts->st.main->flags.blocked; if (blocked) { char *txt; /* main was blocked and nobody can send */ if (blocked < 0) txt = "the main tasklet is receiving" " without a sender available."; else txt = "the main tasklet is sending" " without a receiver available."; PyErr_SetString(PyExc_StopIteration, txt); /* fall through to error handling */ retval = slp_curexc_to_bomb(); if (retval == NULL) return NULL; } next = ts->st.main; } if (PyBomb_Check(retval)) { /* * error handling: continue in the context of the main tasklet. */ next = ts->st.main; TASKLET_SETVAL(next, retval); } return schedule_task_destruct(task, next); }