// 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; } }
// 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; }
void generatorDestructor(Box* b) { assert(isSubclass(b->cls, generator_cls)); BoxedGenerator* self = static_cast<BoxedGenerator*>(b); freeGeneratorStack(self); }