Skip to content

Commit 4b82b8c

Browse files
committed
Rename field to generator_return_kind
Also use it in both the default and FT builds.
1 parent f532899 commit 4b82b8c

File tree

8 files changed

+35
-36
lines changed

8 files changed

+35
-36
lines changed

Include/internal/pycore_tstate.h

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,13 @@ typedef struct _PyThreadStateImpl {
8282
PyObject *asyncio_running_loop; // Strong reference
8383
PyObject *asyncio_running_task; // Strong reference
8484

85+
// Distinguishes between yield and return from PyEval_EvalFrame().
86+
// See gen_send_ex2() in Objects/genobject.c
87+
enum {
88+
GENERATOR_RETURN = 0,
89+
GENERATOR_YIELD = 1,
90+
} generator_return_kind;
91+
8592
/* Head of circular linked-list of all tasks which are instances of `asyncio.Task`
8693
or subclasses of it used in `asyncio.all_tasks`.
8794
*/
@@ -113,10 +120,6 @@ typedef struct _PyThreadStateImpl {
113120
// When >1, code objects do not immortalize their non-string constants.
114121
int suppress_co_const_immortalization;
115122

116-
// Last known frame state for generators/coroutines in this thread
117-
// Used by genobject.c
118-
int8_t gen_last_frame_state;
119-
120123
#ifdef Py_STATS
121124
// per-thread stats, will be merged into interp->pystats_struct
122125
PyStats *pystats_struct; // allocated by _PyStats_ThreadInit()

Objects/genobject.c

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -235,32 +235,36 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc)
235235
_PyErr_ChainStackItem();
236236
}
237237

238-
#if defined(Py_GIL_DISABLED)
239-
((_PyThreadStateImpl *)tstate)->gen_last_frame_state = FRAME_EXECUTING;
240-
#endif
238+
// The generator_return_kind field is used to distinguish between a
239+
// yield and a return from within _PyEval_EvalFrame(). Earlier versions
240+
// of CPython (prior to 3.15) used gi_frame_state for this purpose, but
241+
// that requires the GIL for thread-safety.
242+
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_RETURN;
241243

242244
EVAL_CALL_STAT_INC(EVAL_CALL_GENERATOR);
243245
PyObject *result = _PyEval_EvalFrame(tstate, frame, exc);
244246
assert(tstate->exc_info == prev_exc_info);
245-
#ifdef Py_GIL_DISABLED
246-
// Grab the last frame state from the thread state instead of the
247-
// generator, as it may have changed if another thread resumed this
248-
// generator.
249-
int8_t frame_state = ((_PyThreadStateImpl *)tstate)->gen_last_frame_state;
250-
#else
247+
#ifndef Py_GIL_DISABLED
251248
assert(gen->gi_exc_state.previous_item == NULL);
252249
assert(frame->previous == NULL);
253-
int8_t frame_state = gen->gi_frame_state;
250+
assert(gen->gi_frame_state != FRAME_EXECUTING);
254251
#endif
255-
assert(frame_state != FRAME_EXECUTING);
252+
253+
int return_kind = ((_PyThreadStateImpl *)tstate)->generator_return_kind;
254+
255+
if (return_kind == GENERATOR_YIELD) {
256+
assert(result != NULL && !_PyErr_Occurred(tstate));
257+
*presult = result;
258+
return PYGEN_NEXT;
259+
}
260+
261+
assert(return_kind == GENERATOR_RETURN);
262+
assert(gen->gi_exc_state.exc_value == NULL);
263+
assert(FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state) == FRAME_CLEARED);
256264

257265
/* If the generator just returned (as opposed to yielding), signal
258266
* that the generator is exhausted. */
259267
if (result) {
260-
if (FRAME_STATE_SUSPENDED(frame_state)) {
261-
*presult = result;
262-
return PYGEN_NEXT;
263-
}
264268
assert(result == Py_None || !PyAsyncGen_CheckExact(gen));
265269
if (result == Py_None && !PyAsyncGen_CheckExact(gen) && !arg) {
266270
/* Return NULL if called by gen_iternext() */
@@ -273,8 +277,6 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc)
273277
!PyErr_ExceptionMatches(PyExc_StopAsyncIteration));
274278
}
275279

276-
assert(gen->gi_exc_state.exc_value == NULL);
277-
assert(frame_state == FRAME_CLEARED);
278280
*presult = result;
279281
return result ? PYGEN_RETURN : PYGEN_ERROR;
280282
}

Python/bytecodes.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1392,7 +1392,8 @@ dummy_func(
13921392
_PyInterpreterFrame *gen_frame = frame;
13931393
frame = tstate->current_frame = frame->previous;
13941394
gen_frame->previous = NULL;
1395-
gen_set_frame_state(gen, tstate, FRAME_SUSPENDED + oparg);
1395+
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
1396+
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg);
13961397
/* We don't know which of these is relevant here, so keep them equal */
13971398
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
13981399
#if TIER_ONE

Python/ceval.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2304,7 +2304,7 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame)
23042304
{
23052305
assert(frame->owner == FRAME_OWNED_BY_GENERATOR);
23062306
PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame);
2307-
gen_set_frame_state(gen, tstate, FRAME_CLEARED);
2307+
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_CLEARED);
23082308
assert(tstate->exc_info == &gen->gi_exc_state);
23092309
tstate->exc_info = gen->gi_exc_state.previous_item;
23102310
gen->gi_exc_state.previous_item = NULL;

Python/ceval_macros.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -521,12 +521,3 @@ gen_try_set_executing(PyGenObject *gen)
521521
}
522522
return false;
523523
}
524-
525-
static inline void
526-
gen_set_frame_state(PyGenObject *gen, PyThreadState *tstate, int8_t frame_state)
527-
{
528-
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, frame_state);
529-
#ifdef Py_GIL_DISABLED
530-
((_PyThreadStateImpl *)tstate)->gen_last_frame_state = frame_state;
531-
#endif
532-
}

Python/executor_cases.c.h

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Python/generated_cases.c.h

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Tools/cases_generator/analyzer.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,6 @@ def has_error_without_pop(op: parser.CodeDef) -> bool:
643643
"_PyFunction_SetVersion",
644644
"_PyGen_GetGeneratorFromFrame",
645645
"gen_try_set_executing",
646-
"gen_set_frame_state",
647646
"_PyInterpreterState_GET",
648647
"_PyList_AppendTakeRef",
649648
"_PyList_ITEMS",

0 commit comments

Comments
 (0)