1010#include "pycore_gc.h" // _PyGC_CLEAR_FINALIZED()
1111#include "pycore_genobject.h" // _PyGen_SetStopIterationValue()
1212#include "pycore_interpframe.h" // _PyFrame_GetCode()
13+ #include "pycore_lock.h" // _Py_yield()
1314#include "pycore_modsupport.h" // _PyArg_CheckPositional()
1415#include "pycore_object.h" // _PyObject_GC_UNTRACK()
1516#include "pycore_opcode_utils.h" // RESUME_AFTER_YIELD_FROM
@@ -44,6 +45,18 @@ static PyObject* async_gen_athrow_new(PyAsyncGenObject *, PyObject *);
4445 ((gen)->gi_frame_state = (state), true)
4546#endif
4647
48+ // Wait for any in-progress gi_yieldfrom read to complete.
49+ static inline void
50+ gen_yield_from_lock_wait (PyGenObject * gen , int8_t * frame_state )
51+ {
52+ #ifdef Py_GIL_DISABLED
53+ while (* frame_state == FRAME_SUSPENDED_YIELD_FROM_LOCKED ) {
54+ _Py_yield ();
55+ * frame_state = _Py_atomic_load_int8_relaxed (& gen -> gi_frame_state );
56+ }
57+ #endif
58+ }
59+
4760
4861static const char * NON_INIT_CORO_MSG = "can't send non-None value to a "
4962 "just-started coroutine" ;
@@ -318,6 +331,8 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, PyObject **presult)
318331 * presult = NULL ;
319332 int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state );
320333 do {
334+ gen_yield_from_lock_wait (gen , & frame_state );
335+
321336 if (frame_state == FRAME_CREATED && arg && arg != Py_None ) {
322337 const char * msg = "can't send non-None value to a "
323338 "just-started generator" ;
@@ -452,6 +467,8 @@ gen_close(PyObject *self, PyObject *args)
452467
453468 int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state );
454469 do {
470+ gen_yield_from_lock_wait (gen , & frame_state );
471+
455472 if (frame_state == FRAME_CREATED ) {
456473 // && (1) to avoid -Wunreachable-code warning on Clang
457474 if (!_Py_GEN_TRY_SET_FRAME_STATE (gen , frame_state , FRAME_CLEARED ) && (1 )) {
@@ -470,9 +487,7 @@ gen_close(PyObject *self, PyObject *args)
470487 return NULL ;
471488 }
472489
473- assert (frame_state == FRAME_SUSPENDED_YIELD_FROM ||
474- frame_state == FRAME_SUSPENDED );
475-
490+ assert (FRAME_STATE_SUSPENDED (frame_state ));
476491 } while (!_Py_GEN_TRY_SET_FRAME_STATE (gen , frame_state , FRAME_EXECUTING ));
477492
478493 int err = 0 ;
@@ -614,6 +629,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
614629{
615630 int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state );
616631 do {
632+ gen_yield_from_lock_wait (gen , & frame_state );
633+
617634 if (frame_state == FRAME_EXECUTING ) {
618635 gen_raise_already_executing_error (gen );
619636 return NULL ;
@@ -876,12 +893,25 @@ static PyObject *
876893gen_getyieldfrom (PyObject * self , void * Py_UNUSED (ignored ))
877894{
878895 PyGenObject * gen = _PyGen_CAST (self );
879- int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED (gen -> gi_frame_state );
896+ #ifdef Py_GIL_DISABLED
897+ int8_t frame_state = _Py_atomic_load_int8_relaxed (& gen -> gi_frame_state );
898+ do {
899+ gen_yield_from_lock_wait (gen , & frame_state );
900+ if (frame_state != FRAME_SUSPENDED_YIELD_FROM ) {
901+ Py_RETURN_NONE ;
902+ }
903+ } while (!_Py_GEN_TRY_SET_FRAME_STATE (gen , frame_state , FRAME_SUSPENDED_YIELD_FROM_LOCKED ));
904+
905+ PyObject * result = PyStackRef_AsPyObjectNew (_PyFrame_StackPeek (& gen -> gi_iframe ));
906+ _Py_atomic_store_int8_release (& gen -> gi_frame_state , FRAME_SUSPENDED_YIELD_FROM );
907+ return result ;
908+ #else
909+ int8_t frame_state = gen -> gi_frame_state ;
880910 if (frame_state != FRAME_SUSPENDED_YIELD_FROM ) {
881911 Py_RETURN_NONE ;
882912 }
883- // TODO: still not thread-safe with free threading
884913 return PyStackRef_AsPyObjectNew (_PyFrame_StackPeek (& gen -> gi_iframe ));
914+ #endif
885915}
886916
887917
0 commit comments