PyObject * PyStackless_Schedule(PyObject *retval, int remove) { STACKLESS_GETARG(); PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *prev = ts->st.current, *next = prev->next; PyObject *ret = NULL; if (ts->st.main == NULL) return PyStackless_Schedule_M(retval, remove); Py_INCREF(prev); TASKLET_SETVAL(prev, retval); if (remove) { slp_current_remove(); Py_DECREF(prev); } ret = slp_schedule_task(prev, next, stackless); Py_DECREF(prev); return ret; }
static TASKLET_REMOVE_HEAD(impl_tasklet_remove) { PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *hold = ts->st.current; assert(PyTasklet_Check(task)); if (ts->st.main == NULL) return PyTasklet_Remove_M(task); assert(ts->st.current != NULL); if (task->flags.blocked) RUNTIME_ERROR("You cannot remove a blocked tasklet.", -1); if (task == ts->st.current) RUNTIME_ERROR("The current tasklet cannot be removed.", -1); if (task->next == NULL) return 0; ts->st.current = task; slp_current_remove(); ts->st.current = hold; Py_DECREF(task); return 0; }
static int generic_channel_block(PyThreadState *ts, PyObject **result, PyChannelObject *self, int dir, int stackless) { PyTaskletObject *target, *source = ts->st.current; int fail, switched; /* communication 2): there is nobody waiting, so we must switch */ if (source->flags.block_trap) RUNTIME_ERROR("this tasklet does not like to be" " blocked.", -1); if (self->flags.closing) { PyErr_SetString(PyExc_ValueError, "Send/receive operation on a closed channel"); return -1; } slp_current_remove(); slp_channel_insert(self, source, dir, NULL); target = ts->st.current; /* Make sure that the channel will exist past the actual switch, if * we are softswitching. A temporary channel might disappear. */ assert(ts->st.del_post_switch == NULL); if (Py_REFCNT(self)) { ts->st.del_post_switch = (PyObject*)self; Py_INCREF(self); } fail = slp_schedule_task(result, source, target, stackless, &switched); if (fail || !switched) Py_CLEAR(ts->st.del_post_switch); if (fail) { /* undo our tasklet shuffling */ slp_channel_remove(self, source, NULL, NULL); slp_current_unremove(source); } return fail; }
PyObject * PyStackless_Schedule(PyObject *retval, int remove) { STACKLESS_GETARG(); PyThreadState *ts = PyThreadState_GET(); PyTaskletObject *prev = ts->st.current, *next = prev->next; PyObject *ret = NULL; int switched; if (ts->st.main == NULL) return PyStackless_Schedule_M(retval, remove); /* make sure we hold a reference to the previous tasklet */ Py_INCREF(prev); TASKLET_SETVAL(prev, retval); if (remove) { slp_current_remove(); Py_DECREF(prev); if (next == prev) next = 0; /* we were the last runnable tasklet */ } /* we mustn't DECREF prev here (after the slp_schedule_task(). * This could be the last reference, thus * promting emergency reactivation of the tasklet, * and soft switching isn't really done until we have unwound. * Use the delayed release mechanism instead. */ assert(ts->st.del_post_switch == NULL); ts->st.del_post_switch = (PyObject*)prev; ret = slp_schedule_task(prev, next, stackless, &switched); /* however, if this was a no-op (e.g. prev==next, or an error occurred) * we need to decref prev ourselves */ if (!switched) Py_CLEAR(ts->st.del_post_switch); return ret; }
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); }
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 *retval; int runflags = 0; assert(abs(dir) == 1); 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 */ target = slp_channel_remove(self, -dir); /* exchange data */ TASKLET_SWAPVAL(source, target); if (interthread) { ; /* interthread, always keep target! slp_current_insert(target);*/ } else { if (self->flags.schedule_all) { /* target goes last */ slp_current_insert(target); /* always schedule away from source */ target = source->next; } else if (self->flags.preference == -dir) { /* move target after source */ ts->st.current = source->next; slp_current_insert(target); ts->st.current = source; /* don't mess with this scheduling behaviour: */ runflags = PY_WATCHDOG_NO_SOFT_IRQ; } else { /* otherwise we return to the caller */ slp_current_insert(target); target = source; /* don't mess with this scheduling behaviour: */ runflags = PY_WATCHDOG_NO_SOFT_IRQ; } } } else { /* communication 2): there is nobody waiting, so we must switch */ if (source->flags.block_trap) RUNTIME_ERROR("this tasklet does not like to be" " blocked.", NULL); if (self->flags.closing) { PyErr_SetNone(PyExc_StopIteration); return NULL; } slp_current_remove(); slp_channel_insert(self, source, dir); target = ts->st.current; /* Make sure that the channel will exist past the actual switch, if * we are softswitching. A temporary channel might disappear. */ if (Py_REFCNT(self)) { assert(ts->st.del_post_switch == NULL); ts->st.del_post_switch = (PyObject*)self; Py_INCREF(self); } } ts->st.runflags |= runflags; /* extra info for slp_schedule_task */ retval = slp_schedule_task(source, target, stackless, 0); if (interthread) { if (cando) { Py_DECREF(target); } NOTIFY_CHANNEL(self, source, dir, cando, NULL); } return retval; }
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 *retval; assert(abs(dir) == 1); 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 */ target = slp_channel_remove(self, -dir); /* exchange data */ TASKLET_SWAPVAL(source, target); if (interthread) { /* interthread, always keep target! */ slp_current_insert(target); } else { if (self->flags.schedule_all) { /* target goes last */ slp_current_insert(target); /* always schedule away from source */ target = source->next; } else if (self->flags.preference == -dir) { /* move target after source */ ts->st.current = source->next; slp_current_insert(target); ts->st.current = source; } else { /* otherwise we return to the caller */ slp_current_insert(target); target = source; } } } else { /* communication 2): there is nobody waiting */ if (source->flags.block_trap) RUNTIME_ERROR("this tasklet does not like to be" " blocked.", NULL); if (self->flags.closing) { PyErr_SetNone(PyExc_StopIteration); return NULL; } slp_current_remove(); slp_channel_insert(self, source, dir); target = ts->st.current; } retval = slp_schedule_task(source, target, stackless); if (interthread) NOTIFY_CHANNEL(self, source, dir, cando, NULL); return retval; }