|
10 | 10 | #include "pycore_gc.h" // _PyGC_CLEAR_FINALIZED() |
11 | 11 | #include "pycore_genobject.h" // _PyGen_SetStopIterationValue() |
12 | 12 | #include "pycore_interpframe.h" // _PyFrame_GetCode() |
| 13 | +#include "pycore_lock.h" // _Py_yield() |
13 | 14 | #include "pycore_modsupport.h" // _PyArg_CheckPositional() |
14 | 15 | #include "pycore_object.h" // _PyObject_GC_UNTRACK() |
15 | 16 | #include "pycore_opcode_utils.h" // RESUME_AFTER_YIELD_FROM |
@@ -44,6 +45,18 @@ static PyObject* async_gen_athrow_new(PyAsyncGenObject *, PyObject *); |
44 | 45 | ((gen)->gi_frame_state = (state), true) |
45 | 46 | #endif |
46 | 47 |
|
| 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 | + |
47 | 60 |
|
48 | 61 | static const char *NON_INIT_CORO_MSG = "can't send non-None value to a " |
49 | 62 | "just-started coroutine"; |
@@ -318,6 +331,8 @@ gen_send_ex(PyGenObject *gen, PyObject *arg, PyObject **presult) |
318 | 331 | *presult = NULL; |
319 | 332 | int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); |
320 | 333 | do { |
| 334 | + gen_yield_from_lock_wait(gen, &frame_state); |
| 335 | + |
321 | 336 | if (frame_state == FRAME_CREATED && arg && arg != Py_None) { |
322 | 337 | const char *msg = "can't send non-None value to a " |
323 | 338 | "just-started generator"; |
@@ -452,6 +467,8 @@ gen_close(PyObject *self, PyObject *args) |
452 | 467 |
|
453 | 468 | int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); |
454 | 469 | do { |
| 470 | + gen_yield_from_lock_wait(gen, &frame_state); |
| 471 | + |
455 | 472 | if (frame_state == FRAME_CREATED) { |
456 | 473 | // && (1) to avoid -Wunreachable-code warning on Clang |
457 | 474 | if (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_CLEARED) && (1)) { |
@@ -614,6 +631,8 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, |
614 | 631 | { |
615 | 632 | int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); |
616 | 633 | do { |
| 634 | + gen_yield_from_lock_wait(gen, &frame_state); |
| 635 | + |
617 | 636 | if (frame_state == FRAME_EXECUTING) { |
618 | 637 | gen_raise_already_executing_error(gen); |
619 | 638 | return NULL; |
@@ -876,12 +895,25 @@ static PyObject * |
876 | 895 | gen_getyieldfrom(PyObject *self, void *Py_UNUSED(ignored)) |
877 | 896 | { |
878 | 897 | PyGenObject *gen = _PyGen_CAST(self); |
879 | | - int8_t frame_state = FT_ATOMIC_LOAD_INT8_RELAXED(gen->gi_frame_state); |
| 898 | +#ifdef Py_GIL_DISABLED |
| 899 | + int8_t frame_state = _Py_atomic_load_int8_relaxed(&gen->gi_frame_state); |
| 900 | + do { |
| 901 | + gen_yield_from_lock_wait(gen, &frame_state); |
| 902 | + if (frame_state != FRAME_SUSPENDED_YIELD_FROM) { |
| 903 | + Py_RETURN_NONE; |
| 904 | + } |
| 905 | + } while (!_Py_GEN_TRY_SET_FRAME_STATE(gen, frame_state, FRAME_SUSPENDED_YIELD_FROM_LOCKED)); |
| 906 | + |
| 907 | + PyObject *result = PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(&gen->gi_iframe)); |
| 908 | + _Py_atomic_store_int8_release(&gen->gi_frame_state, FRAME_SUSPENDED_YIELD_FROM); |
| 909 | + return result; |
| 910 | +#else |
| 911 | + int8_t frame_state = gen->gi_frame_state; |
880 | 912 | if (frame_state != FRAME_SUSPENDED_YIELD_FROM) { |
881 | 913 | Py_RETURN_NONE; |
882 | 914 | } |
883 | | - // TODO: still not thread-safe with free threading |
884 | 915 | return PyStackRef_AsPyObjectNew(_PyFrame_StackPeek(&gen->gi_iframe)); |
| 916 | +#endif |
885 | 917 | } |
886 | 918 |
|
887 | 919 |
|
|
0 commit comments