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;
}
Beispiel #3
0
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;
}
Beispiel #4
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);
}