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;
}