Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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);
}
Ejemplo n.º 6
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 *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;
}
Ejemplo n.º 7
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 *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;
}