Ejemplo n.º 1
0
void generatorEntry(BoxedGenerator* g) {
    {
        assert(g->cls == generator_cls);
        assert(g->function->cls == function_cls);

        threading::pushGenerator(g, g->stack_begin, g->returnContext);
        try {
            RegisterHelper context_registerer(g, __builtin_frame_address(0));

            // call body of the generator
            BoxedFunctionBase* func = g->function;

            Box** args = g->args ? &g->args->elts[0] : nullptr;
            callCLFunc<ExceptionStyle::CXX>(func->f, nullptr, func->f->numReceivedArgs(), func->closure, g,
                                            func->globals, g->arg1, g->arg2, g->arg3, args);
        } catch (ExcInfo e) {
            // unhandled exception: propagate the exception to the caller
            g->exception = e;
        }

        // we returned from the body of the generator. next/send/throw will notify the caller
        g->entryExited = true;
        threading::popGenerator();
    }
    swapContext(&g->context, g->returnContext, 0);
}
Ejemplo n.º 2
0
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called)
static void generatorSendInternal(BoxedGenerator* self, Box* v) {
    STAT_TIMER(t0, "us_timer_generator_switching", 0);

    if (self->running)
        raiseExcHelper(ValueError, "generator already executing");

    // check if the generator already exited
    if (self->entryExited) {
        freeGeneratorStack(self);
        raiseExcHelper(StopIteration, (const char*)nullptr);
    }

    self->returnValue = v;
    self->running = true;

#if STAT_TIMERS
    if (!self->prev_stack)
        self->prev_stack = StatTimer::createStack(self->my_timer);
    else
        self->prev_stack = StatTimer::swapStack(self->prev_stack);
#endif

    swapContext(&self->returnContext, self->context, (intptr_t)self);

#if STAT_TIMERS
    self->prev_stack = StatTimer::swapStack(self->prev_stack);
    if (self->entryExited) {
        assert(self->prev_stack == &self->my_timer);
        assert(self->my_timer.isPaused());
    }
#endif

    self->running = false;

    // propagate exception to the caller
    if (self->exception.type) {
        assert(self->entryExited);
        freeGeneratorStack(self);
        // don't raise StopIteration exceptions because those are handled specially.
        if (!self->exception.matches(StopIteration))
            throw self->exception;
        return;
    }

    if (self->entryExited) {
        freeGeneratorStack(self);
        // Reset the current exception.
        // We could directly create the StopIteration exception but we delay creating it because often the caller is not
        // interested in the exception (=generatorHasnext). If we really need it we will create it inside generatorSend.
        self->exception = ExcInfo(NULL, NULL, NULL);
        return;
    }
}
Ejemplo n.º 3
0
extern "C" Box* yield(BoxedGenerator* obj, Box* value) {
    STAT_TIMER(t0, "us_timer_generator_switching", 0);

    assert(obj->cls == generator_cls);
    BoxedGenerator* self = static_cast<BoxedGenerator*>(obj);
    self->returnValue = value;

    threading::popGenerator();
    swapContext(&self->context, self->returnContext, 0);
    threading::pushGenerator(obj, obj->stack_begin, obj->returnContext);

    // if the generator receives a exception from the caller we have to throw it
    if (self->exception.type) {
        ExcInfo e = self->exception;
        self->exception = ExcInfo(NULL, NULL, NULL);
        throw e;
    }
    return self->returnValue;
}
Ejemplo n.º 4
0
void generatorEntry(BoxedGenerator* g) noexcept {
    {
        assert(g->cls == generator_cls);
        assert(g->function->cls == function_cls);

        assert(g->returnValue == Py_None);
        Py_CLEAR(g->returnValue);

        {
            RegisterHelper context_registerer(g, __builtin_frame_address(0));

            g->top_caller_frame_info = (FrameInfo*)cur_thread_state.frame_info;

            // call body of the generator
            BoxedFunctionBase* func = g->function;
            // unnecessary because the generator owns g->function
            // KEEP_ALIVE(func);

            Box** args = g->args ? &g->args->elts[0] : nullptr;
            auto r = callCLFunc<ExceptionStyle::CAPI, NOT_REWRITABLE>(func->code, nullptr,
                     func->code->numReceivedArgs(), func->closure, g,
                     func->globals, g->arg1, g->arg2, g->arg3, args);
            if (r)
                Py_DECREF(r);
            else {
                // unhandled exception: propagate the exception to the caller
                PyErr_Fetch(&g->exception.type, &g->exception.value, &g->exception.traceback);
                PyErr_Clear();
            }
        }

        // we returned from the body of the generator. next/send/throw will notify the caller
        g->entryExited = true;
    }
    assert(g->top_caller_frame_info == cur_thread_state.frame_info);
    swapContext(&g->context, g->returnContext, 0);
}
Ejemplo n.º 5
0
// called from both generatorHasNext and generatorSend/generatorNext (but only if generatorHasNext hasn't been called)
template <ExceptionStyle S> static bool generatorSendInternal(BoxedGenerator* self, Box* v) noexcept(S == CAPI) {
    STAT_TIMER(t0, "us_timer_generator_switching", 0);

    if (!self->returnContext && v != Py_None) {
        if (S == CAPI) {
            PyErr_SetString(TypeError, "can't send non-None value to a just-started generator");
            return true;
        } else
            raiseExcHelper(TypeError, "can't send non-None value to a just-started generator");
    }

    if (self->running) {
        if (S == CAPI) {
            PyErr_SetString(ValueError, "generator already executing");
            return true;
        } else
            raiseExcHelper(ValueError, "generator already executing");
    }

    // check if the generator already exited
    if (self->entryExited) {
        freeGeneratorStack(self);
        if (S == CAPI) {
            PyErr_SetObject(StopIteration, Py_None);
            return true;
        } else
            raiseExcHelper(StopIteration, (const char*)nullptr);
    }

    assert(!self->returnValue);
    self->returnValue = incref(v);
    self->running = true;

#if STAT_TIMERS
    if (!self->prev_stack)
        self->prev_stack = StatTimer::createStack(self->my_timer);
    else
        self->prev_stack = StatTimer::swapStack(self->prev_stack);
#endif
    auto* top_caller_frame_info = (FrameInfo*)cur_thread_state.frame_info;
    swapContext(&self->returnContext, self->context, (intptr_t)self);
    assert(cur_thread_state.frame_info == top_caller_frame_info
           && "the generator should reset the frame info before the swapContext");


#if STAT_TIMERS
    self->prev_stack = StatTimer::swapStack(self->prev_stack);
    if (self->entryExited) {
        assert(self->prev_stack == &self->my_timer);
        assert(self->my_timer.isPaused());
    }
#endif

    self->running = false;

    // propagate exception to the caller
    if (self->exception.type) {
        freeGeneratorStack(self);
        // don't raise StopIteration exceptions because those are handled specially.
        if (!self->exception.matches(StopIteration)) {
            if (S == CAPI) {
                setCAPIException(self->exception);
                self->exception = ExcInfo(NULL, NULL, NULL);
                return true;
            } else {
                auto exc = self->exception;
                self->exception = ExcInfo(NULL, NULL, NULL);
                throw exc;
            }
        }
        return false;
    }

    if (self->entryExited) {
        freeGeneratorStack(self);
        // Reset the current exception.
        // We could directly create the StopIteration exception but we delay creating it because often the caller is not
        // interested in the exception (=generatorHasnext). If we really need it we will create it inside generatorSend.
        assert(!self->exception.type && "need to decref existing exception");
        self->exception = ExcInfo(NULL, NULL, NULL);
        return false;
    }
    return false;
}