Skip to content

Commit c88a831

Browse files
DinoVmeta-codesync[bot]
authored andcommitted
Updates for generator changes
Summary: Generators changed upstream to fix some issues with free-threading: python/cpython@08bc03f This merges the interpreter loop changes that were present in our copied bytecodes, brings in `gen_try_set_executing` from ceval_macros.h and wraps gen_getyieldfrom which is now where the implementation of _PyGen_yf lives. There's also some changes in `PyEval_GetAwaitable` which we current "borrow" by copying it. But the only thing we change is `_PyCoro_GetAwaitableIter` -> `JitCoro_GetAwaitableIter`. So this replaces it with a real borrow with ifdef's to rename things, that way we won't need to perform as many updates in the future. Reviewed By: itamaro Differential Revision: D90126048 fbshipit-source-id: 1e27d7697d4358b1f0b2e384a14e39f842f9665e
1 parent 3314292 commit c88a831

File tree

7 files changed

+129
-127
lines changed

7 files changed

+129
-127
lines changed

cinderx/Interpreter/3.15/Includes/ceval_macros.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,32 @@ check_periodics(PyThreadState *tstate) {
419419
return 0;
420420
}
421421

422+
// Mark the generator as executing. Returns true if the state was changed,
423+
// false if it was already executing or finished.
424+
static inline bool
425+
gen_try_set_executing(PyGenObject *gen)
426+
{
427+
#ifdef Py_GIL_DISABLED
428+
if (!_PyObject_IsUniquelyReferenced((PyObject *)gen)) {
429+
int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state);
430+
while (frame_state < FRAME_EXECUTING) {
431+
if (_Py_atomic_compare_exchange_int8(&gen->gi_frame_state,
432+
&frame_state,
433+
FRAME_EXECUTING)) {
434+
return true;
435+
}
436+
}
437+
}
438+
#endif
439+
// Use faster non-atomic modifications in the GIL-enabled build and when
440+
// the object is uniquely referenced in the free-threaded build.
441+
if (gen->gi_frame_state < FRAME_EXECUTING) {
442+
gen->gi_frame_state = FRAME_EXECUTING;
443+
return true;
444+
}
445+
return false;
446+
}
447+
422448
// CO_NO_MONITORING_EVENTS indicates the code object is read-only and therefore
423449
// cannot have code-extra data added.
424450
#define CI_SET_ADAPTIVE_INTERPRETER_ENABLED_STATE \

cinderx/Interpreter/3.15/Includes/generated_cases.c.h

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9014,7 +9014,6 @@
90149014
PyGenObject* gen = _PyGen_GetGeneratorFromFrame(frame);
90159015
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
90169016
assert(oparg == 0 || oparg == 1);
9017-
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
90189017
_PyStackRef temp = retval;
90199018
stack_pointer += -1;
90209019
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
@@ -9026,6 +9025,8 @@
90269025
frame = tstate->current_frame = frame->previous;
90279026
CI_SET_ADAPTIVE_INTERPRETER_ENABLED_STATE
90289027
gen_frame->previous = NULL;
9028+
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
9029+
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg);
90299030
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
90309031
#if TIER_ONE
90319032
assert(
@@ -12031,19 +12032,18 @@
1203112032
// _SEND
1203212033
{
1203312034
v = stack_pointer[-1];
12034-
PyObject* receiver_o = PyStackRef_AsPyObjectBorrow(receiver);
12035-
PyObject* retval_o;
12035+
PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver);
12036+
PyObject *retval_o;
1203612037
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
1203712038
if (!IS_PEP523_HOOKED(tstate) &&
12038-
(Py_TYPE(receiver_o) == &PyGen_Type ||
12039-
Py_TYPE(receiver_o) == &PyCoro_Type) &&
12040-
((PyGenObject*)receiver_o)->gi_frame_state < FRAME_EXECUTING) {
12041-
PyGenObject* gen = (PyGenObject*)receiver_o;
12042-
_PyInterpreterFrame* gen_frame = &gen->gi_iframe;
12039+
(Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) &&
12040+
gen_try_set_executing((PyGenObject *)receiver_o))
12041+
{
12042+
PyGenObject *gen = (PyGenObject *)receiver_o;
12043+
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
1204312044
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
1204412045
stack_pointer += -1;
1204512046
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
12046-
gen->gi_frame_state = FRAME_EXECUTING;
1204712047
gen->gi_exc_state.previous_item = tstate->exc_info;
1204812048
tstate->exc_info = &gen->gi_exc_state;
1204912049
assert( 2u + oparg <= UINT16_MAX);
@@ -12056,10 +12056,12 @@
1205612056
_PyFrame_SetStackPointer(frame, stack_pointer);
1205712057
retval_o = Py_TYPE(receiver_o)->tp_iternext(receiver_o);
1205812058
stack_pointer = _PyFrame_GetStackPointer(frame);
12059-
} else {
12059+
}
12060+
else {
1206012061
_PyFrame_SetStackPointer(frame, stack_pointer);
12061-
retval_o = PyObject_CallMethodOneArg(
12062-
receiver_o, &_Py_ID(send), PyStackRef_AsPyObjectBorrow(v));
12062+
retval_o = PyObject_CallMethodOneArg(receiver_o,
12063+
&_Py_ID(send),
12064+
PyStackRef_AsPyObjectBorrow(v));
1206312065
stack_pointer = _PyFrame_GetStackPointer(frame);
1206412066
}
1206512067
if (retval_o == NULL) {
@@ -12077,7 +12079,8 @@
1207712079
if (err == 0) {
1207812080
assert(retval_o != NULL);
1207912081
JUMPBY(oparg);
12080-
} else {
12082+
}
12083+
else {
1208112084
stack_pointer += -1;
1208212085
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
1208312086
_PyFrame_SetStackPointer(frame, stack_pointer);
@@ -13788,7 +13791,6 @@
1378813791
PyGenObject* gen = _PyGen_GetGeneratorFromFrame(frame);
1378913792
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
1379013793
assert(oparg == 0 || oparg == 1);
13791-
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
1379213794
_PyStackRef temp = retval;
1379313795
stack_pointer += -1;
1379413796
ASSERT_WITHIN_STACK_BOUNDS(__FILE__, __LINE__);
@@ -13800,6 +13802,8 @@
1380013802
frame = tstate->current_frame = frame->previous;
1380113803
CI_SET_ADAPTIVE_INTERPRETER_ENABLED_STATE
1380213804
gen_frame->previous = NULL;
13805+
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
13806+
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg);
1380313807
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
1380413808
#if TIER_ONE
1380513809
assert(

cinderx/Interpreter/3.15/borrowed-ceval.c.template

Lines changed: 8 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -73,70 +73,16 @@
7373
// @Borrow function _PyEvalFramePushAndInit_Ex from Python/ceval.c
7474

7575
// Cinder specific adapted functions
76-
static PyObject *
77-
Ci_PyEval_GetANext(PyObject *aiter)
78-
{
79-
unaryfunc getter = NULL;
80-
PyObject *next_iter = NULL;
81-
PyTypeObject *type = Py_TYPE(aiter);
82-
if (PyAsyncGen_CheckExact(aiter)) {
83-
return type->tp_as_async->am_anext(aiter);
84-
}
85-
if (type->tp_as_async != NULL){
86-
getter = type->tp_as_async->am_anext;
87-
}
76+
#define _PyCoro_GetAwaitableIter JitCoro_GetAwaitableIter
77+
#define _PyEval_GetAwaitable Ci_PyEval_GetAwaitable
78+
#define _PyEval_GetANext Ci_PyEval_GetANext
8879

89-
if (getter != NULL) {
90-
next_iter = (*getter)(aiter);
91-
if (next_iter == NULL) {
92-
return NULL;
93-
}
94-
}
95-
else {
96-
PyErr_Format(PyExc_TypeError,
97-
"'async for' requires an iterator with "
98-
"__anext__ method, got %.100s",
99-
type->tp_name);
100-
return NULL;
101-
}
80+
// @Borrow function _PyEval_GetANext from Python/ceval.c
81+
// @Borrow function _PyEval_GetAwaitable from Python/ceval.c
10282

103-
// CX: Changed from _PyCoro_GetAwaitableIter
104-
PyObject *awaitable = JitCoro_GetAwaitableIter(next_iter);
105-
if (awaitable == NULL) {
106-
_PyErr_FormatFromCause(
107-
PyExc_TypeError,
108-
"'async for' received an invalid object "
109-
"from __anext__: %.100s",
110-
Py_TYPE(next_iter)->tp_name);
111-
}
112-
Py_DECREF(next_iter);
113-
return awaitable;
114-
}
115-
116-
static PyObject *
117-
Ci_PyEval_GetAwaitable(PyObject *iterable, int oparg)
118-
{
119-
// CX: Changed from _PyCoro_GetAwaitableIter
120-
PyObject *iter = JitCoro_GetAwaitableIter(iterable);
121-
122-
if (iter == NULL) {
123-
_PyEval_FormatAwaitableError(PyThreadState_GET(),
124-
Py_TYPE(iterable), oparg);
125-
}
126-
else if (PyCoro_CheckExact(iter)) {
127-
PyObject *yf = _PyGen_yf((PyGenObject*)iter);
128-
if (yf != NULL) {
129-
/* `iter` is a coroutine object that is being
130-
awaited, `yf` is a pointer to the current awaitable
131-
being awaited on. */
132-
Py_DECREF(yf);
133-
Py_CLEAR(iter);
134-
_PyErr_SetString(PyThreadState_GET(), PyExc_RuntimeError,
135-
"coroutine is being awaited already");
136-
}
137-
}
138-
return iter;
139-
}
83+
#undef _PyCoro_GetAwaitableIter
84+
#undef _PyEval_GetAwaitable
85+
#undef _PyEval_GetANext
14086

14187
#if Py_TAIL_CALL_INTERP
14288
#include "cinderx/Interpreter/cinderx_opcode_targets.h"

cinderx/Interpreter/3.15/ceval.h

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1069,8 +1069,12 @@ _PyEvalFramePushAndInit_Ex(PyThreadState *tstate, _PyStackRef func,
10691069
}
10701070

10711071
// Cinder specific adapted functions
1072-
static PyObject *
1073-
Ci_PyEval_GetANext(PyObject *aiter)
1072+
#define _PyCoro_GetAwaitableIter JitCoro_GetAwaitableIter
1073+
#define _PyEval_GetAwaitable Ci_PyEval_GetAwaitable
1074+
#define _PyEval_GetANext Ci_PyEval_GetANext
1075+
1076+
PyObject *
1077+
_PyEval_GetANext(PyObject *aiter)
10741078
{
10751079
unaryfunc getter = NULL;
10761080
PyObject *next_iter = NULL;
@@ -1096,8 +1100,7 @@ Ci_PyEval_GetANext(PyObject *aiter)
10961100
return NULL;
10971101
}
10981102

1099-
// CX: Changed from _PyCoro_GetAwaitableIter
1100-
PyObject *awaitable = JitCoro_GetAwaitableIter(next_iter);
1103+
PyObject *awaitable = _PyCoro_GetAwaitableIter(next_iter);
11011104
if (awaitable == NULL) {
11021105
_PyErr_FormatFromCause(
11031106
PyExc_TypeError,
@@ -1108,32 +1111,32 @@ Ci_PyEval_GetANext(PyObject *aiter)
11081111
Py_DECREF(next_iter);
11091112
return awaitable;
11101113
}
1111-
1112-
static PyObject *
1113-
Ci_PyEval_GetAwaitable(PyObject *iterable, int oparg)
1114+
PyObject *
1115+
_PyEval_GetAwaitable(PyObject *iterable, int oparg)
11141116
{
1115-
// CX: Changed from _PyCoro_GetAwaitableIter
1116-
PyObject *iter = JitCoro_GetAwaitableIter(iterable);
1117+
PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
11171118

11181119
if (iter == NULL) {
11191120
_PyEval_FormatAwaitableError(PyThreadState_GET(),
11201121
Py_TYPE(iterable), oparg);
11211122
}
11221123
else if (PyCoro_CheckExact(iter)) {
1123-
PyObject *yf = _PyGen_yf((PyGenObject*)iter);
1124-
if (yf != NULL) {
1125-
/* `iter` is a coroutine object that is being
1126-
awaited, `yf` is a pointer to the current awaitable
1127-
being awaited on. */
1128-
Py_DECREF(yf);
1124+
PyCoroObject *coro = (PyCoroObject *)iter;
1125+
int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(coro->cr_frame_state);
1126+
if (frame_state == FRAME_SUSPENDED_YIELD_FROM) {
1127+
/* `iter` is a coroutine object that is being awaited. */
11291128
Py_CLEAR(iter);
11301129
_PyErr_SetString(PyThreadState_GET(), PyExc_RuntimeError,
1131-
"coroutine is being awaited already");
1130+
"coroutine is being awaited already");
11321131
}
11331132
}
11341133
return iter;
11351134
}
11361135

1136+
#undef _PyCoro_GetAwaitableIter
1137+
#undef _PyEval_GetAwaitable
1138+
#undef _PyEval_GetANext
1139+
11371140
#if Py_TAIL_CALL_INTERP
11381141
#include "cinderx/Interpreter/cinderx_opcode_targets.h"
11391142
#include "cinderx/Interpreter/3.14/Includes/generated_cases.c.h"

cinderx/Interpreter/3.15/cinder-bytecodes.c

Lines changed: 38 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -455,46 +455,48 @@ static PyObject* dummy_func(
455455
}
456456

457457
override op(_SEND, (receiver, v-- receiver, retval)) {
458-
PyObject* receiver_o = PyStackRef_AsPyObjectBorrow(receiver);
459-
PyObject* retval_o;
458+
PyObject *receiver_o = PyStackRef_AsPyObjectBorrow(receiver);
459+
PyObject *retval_o;
460460
assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
461461
if (!IS_PEP523_HOOKED(tstate) &&
462-
(Py_TYPE(receiver_o) == &PyGen_Type ||
463-
Py_TYPE(receiver_o) == &PyCoro_Type) &&
464-
((PyGenObject*)receiver_o)->gi_frame_state < FRAME_EXECUTING) {
465-
PyGenObject* gen = (PyGenObject*)receiver_o;
466-
_PyInterpreterFrame* gen_frame = &gen->gi_iframe;
467-
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
468-
DEAD(v);
469-
SYNC_SP();
470-
gen->gi_frame_state = FRAME_EXECUTING;
471-
gen->gi_exc_state.previous_item = tstate->exc_info;
472-
tstate->exc_info = &gen->gi_exc_state;
473-
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
474-
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
475-
assert(gen_frame->previous == NULL);
476-
gen_frame->previous = frame;
477-
DISPATCH_INLINED(gen_frame);
462+
(Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) &&
463+
gen_try_set_executing((PyGenObject *)receiver_o))
464+
{
465+
PyGenObject *gen = (PyGenObject *)receiver_o;
466+
_PyInterpreterFrame *gen_frame = &gen->gi_iframe;
467+
_PyFrame_StackPush(gen_frame, PyStackRef_MakeHeapSafe(v));
468+
DEAD(v);
469+
SYNC_SP();
470+
gen->gi_exc_state.previous_item = tstate->exc_info;
471+
tstate->exc_info = &gen->gi_exc_state;
472+
assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX);
473+
frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg);
474+
assert(gen_frame->previous == NULL);
475+
gen_frame->previous = frame;
476+
DISPATCH_INLINED(gen_frame);
478477
}
479478
if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) {
480-
retval_o = Py_TYPE(receiver_o)->tp_iternext(receiver_o);
481-
} else {
482-
retval_o = PyObject_CallMethodOneArg(
483-
receiver_o, &_Py_ID(send), PyStackRef_AsPyObjectBorrow(v));
479+
retval_o = Py_TYPE(receiver_o)->tp_iternext(receiver_o);
480+
}
481+
else {
482+
retval_o = PyObject_CallMethodOneArg(receiver_o,
483+
&_Py_ID(send),
484+
PyStackRef_AsPyObjectBorrow(v));
484485
}
485486
if (retval_o == NULL) {
486-
int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration);
487-
if (matches) {
488-
_PyEval_MonitorRaise(tstate, frame, this_instr);
489-
}
490-
int err = _PyGen_FetchStopIterationValue(&retval_o);
491-
if (err == 0) {
492-
assert(retval_o != NULL);
493-
JUMPBY(oparg);
494-
} else {
495-
PyStackRef_CLOSE(v);
496-
ERROR_IF(true);
497-
}
487+
int matches = _PyErr_ExceptionMatches(tstate, PyExc_StopIteration);
488+
if (matches) {
489+
_PyEval_MonitorRaise(tstate, frame, this_instr);
490+
}
491+
int err = _PyGen_FetchStopIterationValue(&retval_o);
492+
if (err == 0) {
493+
assert(retval_o != NULL);
494+
JUMPBY(oparg);
495+
}
496+
else {
497+
PyStackRef_CLOSE(v);
498+
ERROR_IF(true);
499+
}
498500
}
499501
PyStackRef_CLOSE(v);
500502
retval = PyStackRef_FromPyObjectSteal(retval_o);
@@ -1410,7 +1412,6 @@ static PyObject* dummy_func(
14101412
PyGenObject* gen = _PyGen_GetGeneratorFromFrame(frame);
14111413
assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1);
14121414
assert(oparg == 0 || oparg == 1);
1413-
gen->gi_frame_state = FRAME_SUSPENDED + oparg;
14141415
_PyStackRef temp = retval;
14151416
DEAD(retval);
14161417
SAVE_STACK();
@@ -1424,6 +1425,8 @@ static PyObject* dummy_func(
14241425
CI_SET_ADAPTIVE_INTERPRETER_ENABLED_STATE
14251426

14261427
gen_frame->previous = NULL;
1428+
((_PyThreadStateImpl *)tstate)->generator_return_kind = GENERATOR_YIELD;
1429+
FT_ATOMIC_STORE_INT8_RELEASE(gen->gi_frame_state, FRAME_SUSPENDED + oparg);
14271430
/* We don't know which of these is relevant here, so keep them equal */
14281431
assert(INLINE_CACHE_ENTRIES_SEND == INLINE_CACHE_ENTRIES_FOR_ITER);
14291432
#if TIER_ONE

cinderx/UpstreamBorrow/borrowed-3.15.c.template

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,4 +539,9 @@ void Cix_gen_dealloc_with_custom_free(PyObject* obj) {
539539
gen_dealloc(obj);
540540
}
541541

542+
// @Borrow function gen_getyieldfrom from Objects/genobject.c [3.15]
543+
PyObject * _PyGen_yf(PyGenObject *gen) {
544+
return gen_getyieldfrom((PyObject *)gen, NULL);
545+
}
546+
542547
// @Borrow function _PyTuple_MaybeUntrack from Objects/tupleobject.c [3.15]

0 commit comments

Comments
 (0)