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