diff --git a/Include/internal/pycore_opcode_metadata.h b/Include/internal/pycore_opcode_metadata.h index 5fb236836dccd9..cf6f4d29698a98 100644 --- a/Include/internal/pycore_opcode_metadata.h +++ b/Include/internal/pycore_opcode_metadata.h @@ -2036,7 +2036,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [FORMAT_SIMPLE] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FORMAT_WITH_SPEC] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG }, - [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [FOR_ITER_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [FOR_ITER_LIST] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, [FOR_ITER_RANGE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG | HAS_ERROR_FLAG }, [FOR_ITER_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EXIT_FLAG }, @@ -2134,7 +2134,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [RETURN_GENERATOR] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [RETURN_VALUE] = { true, INSTR_FMT_IX, 0 }, [SEND] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, + [SEND_GEN] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG }, [SETUP_ANNOTATIONS] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [SET_ADD] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [SET_FUNCTION_ATTRIBUTE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, @@ -2170,7 +2170,7 @@ const struct opcode_metadata _PyOpcode_opcode_metadata[266] = { [UNPACK_SEQUENCE_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [UNPACK_SEQUENCE_TWO_TUPLE] = { true, INSTR_FMT_IBC, HAS_ARG_FLAG | HAS_DEOPT_FLAG }, [WITH_EXCEPT_START] = { true, INSTR_FMT_IX, HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, - [YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG }, + [YIELD_VALUE] = { true, INSTR_FMT_IB, HAS_ARG_FLAG | HAS_ESCAPES_FLAG }, [JUMP] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_EVAL_BREAK_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_IF_FALSE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, [JUMP_IF_TRUE] = { true, -1, HAS_ARG_FLAG | HAS_JUMP_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG }, diff --git a/Include/internal/pycore_uop_metadata.h b/Include/internal/pycore_uop_metadata.h index e71194b116e020..7babe4b6ec81ef 100644 --- a/Include/internal/pycore_uop_metadata.h +++ b/Include/internal/pycore_uop_metadata.h @@ -102,8 +102,8 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_GET_AITER] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_GET_ANEXT] = HAS_ERROR_FLAG | HAS_ERROR_NO_POP_FLAG | HAS_ESCAPES_FLAG, [_GET_AWAITABLE] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, - [_SEND_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, - [_YIELD_VALUE] = HAS_ARG_FLAG, + [_SEND_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, + [_YIELD_VALUE] = HAS_ARG_FLAG | HAS_ESCAPES_FLAG, [_POP_EXCEPT] = HAS_ESCAPES_FLAG, [_LOAD_COMMON_CONSTANT] = HAS_ARG_FLAG, [_LOAD_BUILD_CLASS] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, @@ -199,7 +199,7 @@ const uint16_t _PyUop_Flags[MAX_UOP_ID+1] = { [_ITER_CHECK_RANGE] = HAS_EXIT_FLAG, [_GUARD_NOT_EXHAUSTED_RANGE] = HAS_EXIT_FLAG, [_ITER_NEXT_RANGE] = HAS_ERROR_FLAG, - [_FOR_ITER_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG, + [_FOR_ITER_GEN_FRAME] = HAS_ARG_FLAG | HAS_DEOPT_FLAG | HAS_ESCAPES_FLAG, [_LOAD_SPECIAL] = HAS_ARG_FLAG | HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_WITH_EXCEPT_START] = HAS_ERROR_FLAG | HAS_ESCAPES_FLAG, [_PUSH_EXC_INFO] = 0, diff --git a/Lib/test/test_free_threading/test_generators.py b/Lib/test/test_free_threading/test_generators.py new file mode 100644 index 00000000000000..a76aef7e69b684 --- /dev/null +++ b/Lib/test/test_free_threading/test_generators.py @@ -0,0 +1,114 @@ +import unittest + +from unittest import TestCase + +from test import support +from test.support import threading_helper +from test.support import import_helper +from threading import Thread +import types + +@threading_helper.requires_working_threading() +@support.requires_resource("cpu") +class GeneratorFreeThreadingTests(TestCase): + def infinite_generator(self): + def gen(): + while True: + yield + + return gen() + + def infinite_coroutine(self, from_gen: bool = False): + asyncio = import_helper.import_module("asyncio") + + if from_gen: + @types.coroutine + def coro(): + while True: + yield from asyncio.sleep(0) + else: + async def coro(): + while True: + await asyncio.sleep(0) + + return coro() + + def infinite_asyncgen(self): + asyncio = import_helper.import_module("asyncio") + + async def async_gen(): + while True: + await asyncio.sleep(0) + yield 42 + + return async_gen() + + def _stress_object(self, gen, *funcs): + threads = [] + + for func in funcs: + for _ in range(10): + def with_iterations(gen): + for _ in range(100): + func(gen) + + threads.append(Thread(target=with_iterations, args=(gen,))) + + with threading_helper.catch_threading_exception(): + # Errors might come up, but that's fine. + # All we care about is that this doesn't crash. + for thread in threads: + thread.start() + + for thread in threads: + thread.join() + + def stress_generator(self, *funcs): + self._stress_object(self.infinite_generator(), *funcs) + + def stress_coroutine(self, *funcs, from_gen: bool = False): + self._stress_object(self.infinite_coroutine(from_gen=from_gen), *funcs) + + def stress_asyncgen(self, *funcs): + self._stress_object(self.infinite_asyncgen(), *funcs) + + def test_generator_send(self): + self.stress_generator(lambda gen: next(gen)) + + def test_generator_close(self): + self.stress_generator(lambda gen: gen.close()) + self.stress_generator(lambda gen: next(gen), lambda gen: gen.close()) + + def test_generator_state(self): + self.stress_generator(lambda gen: next(gen), lambda gen: gen.gi_running) + self.stress_generator(lambda gen: gen.gi_isrunning, lambda gen: gen.close()) + + def test_coroutine_send(self): + def try_send_non_none(coro): + try: + coro.send(42) + except ValueError: + # Can't send non-None to just started coroutine + coro.send(None) + + self.stress_coroutine(lambda coro: next(coro)) + self.stress_coroutine(try_send_non_none) + + def test_coroutine_attributes(self): + self.stress_coroutine( + lambda coro: next(coro), + lambda coro: coro.cr_frame, + lambda coro: coro.cr_suspended, + ) + + def test_generator_coroutine(self): + self.stress_coroutine(lambda coro: next(coro), from_gen=True) + + def test_async_gen(self): + self.stress_asyncgen(lambda ag: next(ag), lambda ag: ag.send(None)) + self.stress_asyncgen(lambda ag: next(ag), lambda ag: ag.throw(RuntimeError)) + self.stress_asyncgen(lambda ag: next(ag), lambda ag: ag.close()) + self.stress_asyncgen(lambda ag: ag.throw(RuntimeError), lambda ag: ag.close()) + +if __name__ == "__main__": + unittest.main() diff --git a/Lib/test/test_generators.py b/Lib/test/test_generators.py index 2ea6dba12effc1..fe5cb2fe946e99 100644 --- a/Lib/test/test_generators.py +++ b/Lib/test/test_generators.py @@ -10,6 +10,7 @@ import types from test import support +from test.support import threading_helper try: import _testcapi @@ -268,6 +269,47 @@ def loop(): #This should not raise loop() + @support.requires_resource("cpu") + def test_generator_thread_safety(self): + # GH-126369: generators were not thread safe + from threading import Thread, Lock + import contextlib + + def my_generator(): + for i in range(1000): + yield i + + + lock = Lock() + amount = 0 + thread_count = 10 + + def gen_to_list(gen): + # Note: it's intentional to throw out the exception here. Generators + # aren't thread safe, so who knows what will happen. Right now, it just spams + # a lot of ValueError's, but that might change if we decide to make generators + # thread safe in the future. We're just making sure it doesn't crash. + with contextlib.suppress(ValueError): + list(gen) + + with lock: + nonlocal amount + amount += 1 + + generator = my_generator() + with threading_helper.catch_threading_exception() as cm: + ts = [Thread(target=gen_to_list, args=(generator,)) for _ in range(thread_count)] + for t in ts: + t.start() + + for t in ts: + t.join() + + self.assertIsNone(cm.exc_value) + + self.assertEqual(amount, thread_count) + + class ModifyUnderlyingIterableTest(unittest.TestCase): iterables = [ diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-07-15.gh-issue-126366.nd_LOu.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-07-15.gh-issue-126366.nd_LOu.rst new file mode 100644 index 00000000000000..f7c3be98529d18 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-03-15-07-15.gh-issue-126366.nd_LOu.rst @@ -0,0 +1 @@ +Fix possible data races when using :term:`generator iterator` objects concurrently. diff --git a/Objects/clinic/genobject.c.h b/Objects/clinic/genobject.c.h new file mode 100644 index 00000000000000..244822649320a2 --- /dev/null +++ b/Objects/clinic/genobject.c.h @@ -0,0 +1,230 @@ +/*[clinic input] +preserve +[clinic start generated code]*/ + +#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION() + +PyDoc_STRVAR(gen_close_meth__doc__, +"close($self, /)\n" +"--\n" +"\n" +"raise GeneratorExit inside generator."); + +#define GEN_CLOSE_METH_METHODDEF \ + {"close", (PyCFunction)gen_close_meth, METH_NOARGS, gen_close_meth__doc__}, + +static PyObject * +gen_close_meth_impl(PyGenObject *self); + +static PyObject * +gen_close_meth(PyGenObject *self, PyObject *Py_UNUSED(ignored)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = gen_close_meth_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(gen_getrunning_DOCSTR) +# define gen_getrunning_DOCSTR NULL +#endif +#if defined(GEN_GETRUNNING_GETSETDEF) +# undef GEN_GETRUNNING_GETSETDEF +# define GEN_GETRUNNING_GETSETDEF {"gi_running", (getter)gen_getrunning_get, (setter)gen_getrunning_set, gen_getrunning_DOCSTR}, +#else +# define GEN_GETRUNNING_GETSETDEF {"gi_running", (getter)gen_getrunning_get, NULL, gen_getrunning_DOCSTR}, +#endif + +static PyObject * +gen_getrunning_get_impl(PyGenObject *self); + +static PyObject * +gen_getrunning_get(PyGenObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = gen_getrunning_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(gen_getsuspended_DOCSTR) +# define gen_getsuspended_DOCSTR NULL +#endif +#if defined(GEN_GETSUSPENDED_GETSETDEF) +# undef GEN_GETSUSPENDED_GETSETDEF +# define GEN_GETSUSPENDED_GETSETDEF {"gi_suspended", (getter)gen_getsuspended_get, (setter)gen_getsuspended_set, gen_getsuspended_DOCSTR}, +#else +# define GEN_GETSUSPENDED_GETSETDEF {"gi_suspended", (getter)gen_getsuspended_get, NULL, gen_getsuspended_DOCSTR}, +#endif + +static PyObject * +gen_getsuspended_get_impl(PyGenObject *self); + +static PyObject * +gen_getsuspended_get(PyGenObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = gen_getsuspended_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(gen_getframe_DOCSTR) +# define gen_getframe_DOCSTR NULL +#endif +#if defined(GEN_GETFRAME_GETSETDEF) +# undef GEN_GETFRAME_GETSETDEF +# define GEN_GETFRAME_GETSETDEF {"gi_frame", (getter)gen_getframe_get, (setter)gen_getframe_set, gen_getframe_DOCSTR}, +#else +# define GEN_GETFRAME_GETSETDEF {"gi_frame", (getter)gen_getframe_get, NULL, gen_getframe_DOCSTR}, +#endif + +static PyObject * +gen_getframe_get_impl(PyGenObject *self); + +static PyObject * +gen_getframe_get(PyGenObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = gen_getframe_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(cr_getsuspended_DOCSTR) +# define cr_getsuspended_DOCSTR NULL +#endif +#if defined(CR_GETSUSPENDED_GETSETDEF) +# undef CR_GETSUSPENDED_GETSETDEF +# define CR_GETSUSPENDED_GETSETDEF {"cr_suspended", (getter)cr_getsuspended_get, (setter)cr_getsuspended_set, cr_getsuspended_DOCSTR}, +#else +# define CR_GETSUSPENDED_GETSETDEF {"cr_suspended", (getter)cr_getsuspended_get, NULL, cr_getsuspended_DOCSTR}, +#endif + +static PyObject * +cr_getsuspended_get_impl(PyCoroObject *self); + +static PyObject * +cr_getsuspended_get(PyCoroObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = cr_getsuspended_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(cr_getrunning_DOCSTR) +# define cr_getrunning_DOCSTR NULL +#endif +#if defined(CR_GETRUNNING_GETSETDEF) +# undef CR_GETRUNNING_GETSETDEF +# define CR_GETRUNNING_GETSETDEF {"cr_running", (getter)cr_getrunning_get, (setter)cr_getrunning_set, cr_getrunning_DOCSTR}, +#else +# define CR_GETRUNNING_GETSETDEF {"cr_running", (getter)cr_getrunning_get, NULL, cr_getrunning_DOCSTR}, +#endif + +static PyObject * +cr_getrunning_get_impl(PyCoroObject *self); + +static PyObject * +cr_getrunning_get(PyCoroObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = cr_getrunning_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(cr_getframe_DOCSTR) +# define cr_getframe_DOCSTR NULL +#endif +#if defined(CR_GETFRAME_GETSETDEF) +# undef CR_GETFRAME_GETSETDEF +# define CR_GETFRAME_GETSETDEF {"cr_frame", (getter)cr_getframe_get, (setter)cr_getframe_set, cr_getframe_DOCSTR}, +#else +# define CR_GETFRAME_GETSETDEF {"cr_frame", (getter)cr_getframe_get, NULL, cr_getframe_DOCSTR}, +#endif + +static PyObject * +cr_getframe_get_impl(PyCoroObject *self); + +static PyObject * +cr_getframe_get(PyCoroObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = cr_getframe_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(ag_getframe_DOCSTR) +# define ag_getframe_DOCSTR NULL +#endif +#if defined(AG_GETFRAME_GETSETDEF) +# undef AG_GETFRAME_GETSETDEF +# define AG_GETFRAME_GETSETDEF {"ag_frame", (getter)ag_getframe_get, (setter)ag_getframe_set, ag_getframe_DOCSTR}, +#else +# define AG_GETFRAME_GETSETDEF {"ag_frame", (getter)ag_getframe_get, NULL, ag_getframe_DOCSTR}, +#endif + +static PyObject * +ag_getframe_get_impl(PyAsyncGenObject *self); + +static PyObject * +ag_getframe_get(PyAsyncGenObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = ag_getframe_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + +#if !defined(ag_getsuspended_DOCSTR) +# define ag_getsuspended_DOCSTR NULL +#endif +#if defined(AG_GETSUSPENDED_GETSETDEF) +# undef AG_GETSUSPENDED_GETSETDEF +# define AG_GETSUSPENDED_GETSETDEF {"ag_suspended", (getter)ag_getsuspended_get, (setter)ag_getsuspended_set, ag_getsuspended_DOCSTR}, +#else +# define AG_GETSUSPENDED_GETSETDEF {"ag_suspended", (getter)ag_getsuspended_get, NULL, ag_getsuspended_DOCSTR}, +#endif + +static PyObject * +ag_getsuspended_get_impl(PyAsyncGenObject *self); + +static PyObject * +ag_getsuspended_get(PyAsyncGenObject *self, void *Py_UNUSED(context)) +{ + PyObject *return_value = NULL; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = ag_getsuspended_get_impl(self); + Py_END_CRITICAL_SECTION(); + + return return_value; +} +/*[clinic end generated code: output=fb2d54f4bfdabe4f input=a9049054013a1b77]*/ diff --git a/Objects/genobject.c b/Objects/genobject.c index e87f199c2504ba..2a03558c3c9ead 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -17,8 +17,17 @@ #include "pystats.h" +/*[clinic input] +class generator "PyGenObject *" "&PyGen_Type" +class async_generator "PyAsyncGenObject *" "&PyAsyncGen_Type" +class coroutine "PyCoroObject *" "&PyCoro_Type" +[clinic start generated code]*/ +/*[clinic end generated code: output=da39a3ee5e6b4b0d input=593e3f7a2581251e]*/ + +#include "clinic/genobject.c.h" + // Forward declarations -static PyObject* gen_close(PyObject *, PyObject *); +static int gen_close(PyGenObject *gen); static PyObject* async_gen_asend_new(PyAsyncGenObject *, PyObject *); static PyObject* async_gen_athrow_new(PyAsyncGenObject *, PyObject *); @@ -119,15 +128,12 @@ _PyGen_Finalize(PyObject *self) _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } else { - PyObject *res = gen_close((PyObject*)gen, NULL); - if (res == NULL) { + int res = gen_close(gen); + if (res < 0) { if (PyErr_Occurred()) { PyErr_WriteUnraisable(self); } } - else { - Py_DECREF(res); - } } /* Restore the saved exception. */ @@ -175,9 +181,10 @@ gen_dealloc(PyObject *self) } static PySendResult -gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, +gen_send_ex2_lock_held(PyGenObject *gen, PyObject *arg, PyObject **presult, int exc, int closing) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(gen); PyThreadState *tstate = _PyThreadState_GET(); _PyInterpreterFrame *frame = &gen->gi_iframe; @@ -273,6 +280,17 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return result ? PYGEN_RETURN : PYGEN_ERROR; } +static inline PySendResult +gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, + int exc, int closing) +{ + PySendResult result; + Py_BEGIN_CRITICAL_SECTION(gen); + result = gen_send_ex2_lock_held(gen, arg, presult, exc, closing); + Py_END_CRITICAL_SECTION(); + return result; +} + static PySendResult PyGen_am_send(PyObject *self, PyObject *arg, PyObject **result) { @@ -322,10 +340,9 @@ static int gen_close_iter(PyObject *yf) { PyObject *retval = NULL; - if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) { - retval = gen_close((PyObject *)yf, NULL); - if (retval == NULL) + int retval = gen_close(_PyGen_CAST(yf)); + if (retval < 0) return -1; } else { @@ -355,9 +372,10 @@ is_resume(_Py_CODEUNIT *instr) ); } -PyObject * -_PyGen_yf(PyGenObject *gen) +static PyObject * +_PyGen_yf_lock_held(PyGenObject *gen) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(gen); if (gen->gi_frame_state == FRAME_SUSPENDED_YIELD_FROM) { _PyInterpreterFrame *frame = &gen->gi_iframe; // GH-122390: These asserts are wrong in the presence of ENTER_EXECUTOR! @@ -368,10 +386,29 @@ _PyGen_yf(PyGenObject *gen) return NULL; } +PyObject * +_PyGen_yf(PyGenObject *gen) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(gen); + res = _PyGen_yf_lock_held(gen); + Py_END_CRITICAL_SECTION(); + return res; +} + +/*[clinic input] +@critical_section +generator.close as gen_close_meth + +raise GeneratorExit inside generator. +[clinic start generated code]*/ + static PyObject * -gen_close(PyObject *self, PyObject *args) +gen_close_meth_impl(PyGenObject *self) +/*[clinic end generated code: output=b6f489da23415c60 input=f90b586ecfb6d2fa]*/ { PyGenObject *gen = _PyGen_CAST(self); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(gen); if (gen->gi_frame_state == FRAME_CREATED) { gen->gi_frame_state = FRAME_COMPLETED; @@ -435,6 +472,15 @@ gen_close(PyObject *self, PyObject *args) return NULL; } +int gen_close(PyGenObject *gen) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(gen); + res = gen_close_meth_impl(gen); + Py_END_CRITICAL_SECTION(); + assert(res == Py_None || res == NULL); + return res == Py_None ? 0 : -1; +} PyDoc_STRVAR(throw_doc, "throw(value)\n\ @@ -446,9 +492,10 @@ the (type, val, tb) signature is deprecated, \n\ and may be removed in a future version of Python."); static PyObject * -_gen_throw(PyGenObject *gen, int close_on_genexit, +gen_throw_lock_held(PyGenObject *gen, int close_on_genexit, PyObject *typ, PyObject *val, PyObject *tb) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(gen); PyObject *yf = _PyGen_yf(gen); if (yf) { @@ -486,8 +533,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, 'yield from' or awaiting on with 'await'. */ PyFrameState state = gen->gi_frame_state; gen->gi_frame_state = FRAME_EXECUTING; - ret = _gen_throw((PyGenObject *)yf, close_on_genexit, + Py_BEGIN_CRITICAL_SECTION(yf); + ret = gen_throw_lock_held((PyGenObject *)yf, close_on_genexit, typ, val, tb); + Py_END_CRITICAL_SECTION(); gen->gi_frame_state = state; tstate->current_frame = prev; frame->previous = NULL; @@ -604,7 +653,11 @@ gen_throw(PyGenObject *gen, PyObject *const *args, Py_ssize_t nargs) else if (nargs == 2) { val = args[1]; } - return _gen_throw(gen, 1, typ, val, tb); + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(gen); + res = gen_throw_lock_held(gen, 1, typ, val, tb); + Py_END_CRITICAL_SECTION(); + return res; } @@ -749,27 +802,47 @@ gen_getyieldfrom(PyObject *gen, void *Py_UNUSED(ignored)) return yf; } +/*[clinic input] +@getter +@critical_section +generator.gi_running as gen_getrunning +[clinic start generated code]*/ static PyObject * -gen_getrunning(PyObject *self, void *Py_UNUSED(ignored)) +gen_getrunning_get_impl(PyGenObject *self) +/*[clinic end generated code: output=a7233b957ce88a6f input=d3d995cf1581b21b]*/ { - PyGenObject *gen = _PyGen_CAST(self); - if (gen->gi_frame_state == FRAME_EXECUTING) { + if (self->gi_frame_state == FRAME_EXECUTING) { Py_RETURN_TRUE; } Py_RETURN_FALSE; } static PyObject * -gen_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) +gen_getsuspended_lock_held(PyGenObject *self) { - PyGenObject *gen = _PyGen_CAST(self); - return PyBool_FromLong(FRAME_STATE_SUSPENDED(gen->gi_frame_state)); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return PyBool_FromLong(FRAME_STATE_SUSPENDED(self->gi_frame_state)); } +/*[clinic input] +@getter +@critical_section +generator.gi_suspended as gen_getsuspended +[clinic start generated code]*/ + static PyObject * -_gen_getframe(PyGenObject *gen, const char *const name) +gen_getsuspended_get_impl(PyGenObject *self) +/*[clinic end generated code: output=a0345f9be186eda3 input=880e9fb8436726cb]*/ { + return PyBool_FromLong(FRAME_STATE_SUSPENDED(self->gi_frame_state)); +} + +static PyObject * +gen_getframe_lock_held(PyGenObject *self, const char *name) +{ + PyGenObject *gen = _PyGen_CAST(self); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(gen); if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } @@ -779,11 +852,18 @@ _gen_getframe(PyGenObject *gen, const char *const name) return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(&gen->gi_iframe)); } +/*[clinic input] +@critical_section +@getter +generator.gi_frame as gen_getframe +[clinic start generated code]*/ + static PyObject * -gen_getframe(PyObject *self, void *Py_UNUSED(ignored)) +gen_getframe_get_impl(PyGenObject *self) +/*[clinic end generated code: output=69a961dad790fd48 input=0d906f30ab99e1e4]*/ { - PyGenObject *gen = _PyGen_CAST(self); - return _gen_getframe(gen, "gi_frame"); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return gen_getframe_lock_held(self, "gi_frame"); } static PyObject * @@ -809,9 +889,9 @@ static PyGetSetDef gen_getsetlist[] = { PyDoc_STR("qualified name of the generator")}, {"gi_yieldfrom", gen_getyieldfrom, NULL, PyDoc_STR("object being iterated by yield from, or None")}, - {"gi_running", gen_getrunning, NULL, NULL}, - {"gi_frame", gen_getframe, NULL, NULL}, - {"gi_suspended", gen_getsuspended, NULL, NULL}, + GEN_GETRUNNING_GETSETDEF + GEN_GETSUSPENDED_GETSETDEF + GEN_GETFRAME_GETSETDEF {"gi_code", gen_getcode, NULL, NULL}, {NULL} /* Sentinel */ }; @@ -836,7 +916,7 @@ PyDoc_STRVAR(sizeof__doc__, static PyMethodDef gen_methods[] = { {"send", gen_send, METH_O, send_doc}, {"throw", _PyCFunction_CAST(gen_throw), METH_FASTCALL, throw_doc}, - {"close", gen_close, METH_NOARGS, close_doc}, + GEN_CLOSE_METH_METHODDEF {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* Sentinel */ @@ -1125,8 +1205,15 @@ coro_get_cr_await(PyObject *coro, void *Py_UNUSED(ignored)) return yf; } +/*[clinic input] +@getter +@critical_section +coroutine.cr_suspended as cr_getsuspended +[clinic start generated code]*/ + static PyObject * -cr_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) +cr_getsuspended_get_impl(PyCoroObject *self) +/*[clinic end generated code: output=fa37923084e2a87d input=2581134666285807]*/ { PyCoroObject *coro = _PyCoroObject_CAST(self); if (FRAME_STATE_SUSPENDED(coro->cr_frame_state)) { @@ -1135,8 +1222,15 @@ cr_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) Py_RETURN_FALSE; } +/*[clinic input] +@getter +@critical_section +coroutine.cr_running as cr_getrunning +[clinic start generated code]*/ + static PyObject * -cr_getrunning(PyObject *self, void *Py_UNUSED(ignored)) +cr_getrunning_get_impl(PyCoroObject *self) +/*[clinic end generated code: output=153bd71b7b6e4842 input=9b43ec12b69a9f01]*/ { PyCoroObject *coro = _PyCoroObject_CAST(self); if (coro->cr_frame_state == FRAME_EXECUTING) { @@ -1145,10 +1239,18 @@ cr_getrunning(PyObject *self, void *Py_UNUSED(ignored)) Py_RETURN_FALSE; } +/*[clinic input] +@critical_section +@getter +coroutine.cr_frame as cr_getframe +[clinic start generated code]*/ + static PyObject * -cr_getframe(PyObject *coro, void *Py_UNUSED(ignored)) +cr_getframe_get_impl(PyCoroObject *self) +/*[clinic end generated code: output=300f3facb67ebc50 input=32fca6ffc44085b7]*/ { - return _gen_getframe(_PyGen_CAST(coro), "cr_frame"); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return gen_getframe_lock_held(_PyGen_CAST(self), "cr_frame"); } static PyObject * @@ -1165,10 +1267,10 @@ static PyGetSetDef coro_getsetlist[] = { PyDoc_STR("qualified name of the coroutine")}, {"cr_await", coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, - {"cr_running", cr_getrunning, NULL, NULL}, - {"cr_frame", cr_getframe, NULL, NULL}, + CR_GETRUNNING_GETSETDEF + CR_GETFRAME_GETSETDEF {"cr_code", cr_getcode, NULL, NULL}, - {"cr_suspended", cr_getsuspended, NULL, NULL}, + CR_GETSUSPENDED_GETSETDEF {NULL} /* Sentinel */ }; @@ -1197,7 +1299,7 @@ PyDoc_STRVAR(coro_close_doc, static PyMethodDef coro_methods[] = { {"send", gen_send, METH_O, coro_send_doc}, {"throw",_PyCFunction_CAST(gen_throw), METH_FASTCALL, coro_throw_doc}, - {"close", gen_close, METH_NOARGS, coro_close_doc}, + GEN_CLOSE_METH_METHODDEF {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__}, {"__class_getitem__", Py_GenericAlias, METH_O|METH_CLASS, PyDoc_STR("See PEP 585")}, {NULL, NULL} /* Sentinel */ @@ -1296,7 +1398,7 @@ static PyObject * coro_wrapper_close(PyObject *self, PyObject *args) { PyCoroWrapper *cw = _PyCoroWrapper_CAST(self); - return gen_close((PyObject *)cw->cw_coroutine, args); + return gen_close(_PyGen_CAST(cw->cw_coroutine)) == 0 ? Py_None : NULL; } static int @@ -1567,10 +1669,18 @@ async_gen_athrow(PyAsyncGenObject *o, PyObject *args) return async_gen_athrow_new(o, args); } +/*[clinic input] +@critical_section +@getter +async_generator.ag_frame as ag_getframe +[clinic start generated code]*/ + static PyObject * -ag_getframe(PyObject *ag, void *Py_UNUSED(ignored)) +ag_getframe_get_impl(PyAsyncGenObject *self) +/*[clinic end generated code: output=7a31c7181090a4fb input=b059ce210436a682]*/ { - return _gen_getframe((PyGenObject *)ag, "ag_frame"); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return gen_getframe_lock_held(_PyGen_CAST(self), "ag_frame"); } static PyObject * @@ -1579,14 +1689,18 @@ ag_getcode(PyObject *gen, void *Py_UNUSED(ignored)) return _gen_getcode((PyGenObject*)gen, "ag_code"); } +/*[clinic input] +@critical_section +@getter +async_generator.ag_suspended as ag_getsuspended +[clinic start generated code]*/ + static PyObject * -ag_getsuspended(PyObject *self, void *Py_UNUSED(ignored)) +ag_getsuspended_get_impl(PyAsyncGenObject *self) +/*[clinic end generated code: output=f9c6d455edce4c50 input=e463d3db950251a3]*/ { - PyAsyncGenObject *ag = _PyAsyncGenObject_CAST(self); - if (FRAME_STATE_SUSPENDED(ag->ag_frame_state)) { - Py_RETURN_TRUE; - } - Py_RETURN_FALSE; + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); + return gen_getsuspended_lock_held(_PyGen_CAST(self)); } static PyGetSetDef async_gen_getsetlist[] = { @@ -1596,9 +1710,9 @@ static PyGetSetDef async_gen_getsetlist[] = { PyDoc_STR("qualified name of the async generator")}, {"ag_await", coro_get_cr_await, NULL, PyDoc_STR("object being awaited on, or None")}, - {"ag_frame", ag_getframe, NULL, NULL}, + AG_GETFRAME_GETSETDEF {"ag_code", ag_getcode, NULL, NULL}, - {"ag_suspended", ag_getsuspended, NULL, NULL}, + AG_GETSUSPENDED_GETSETDEF {NULL} /* Sentinel */ }; @@ -2086,8 +2200,9 @@ async_gen_athrow_traverse(PyObject *self, visitproc visit, void *arg) static PyObject * -async_gen_athrow_send(PyObject *self, PyObject *arg) +async_gen_athrow_send_lock_held(PyObject *self, PyObject *arg) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); PyAsyncGenAThrow *o = _PyAsyncGenAThrow_CAST(self); PyGenObject *gen = _PyGen_CAST(o->agt_gen); PyObject *retval; @@ -2138,12 +2253,12 @@ async_gen_athrow_send(PyObject *self, PyObject *arg) if (o->agt_args == NULL) { /* aclose() mode */ o->agt_gen->ag_closed = 1; - - retval = _gen_throw((PyGenObject *)gen, + Py_BEGIN_CRITICAL_SECTION(gen); + retval = gen_throw_lock_held((PyGenObject *)gen, 0, /* Do not close generator when PyExc_GeneratorExit is passed */ PyExc_GeneratorExit, NULL, NULL); - + Py_END_CRITICAL_SECTION(); if (retval && _PyAsyncGenWrappedValue_CheckExact(retval)) { Py_DECREF(retval); goto yield_close; @@ -2158,10 +2273,12 @@ async_gen_athrow_send(PyObject *self, PyObject *arg) return NULL; } - retval = _gen_throw((PyGenObject *)gen, + Py_BEGIN_CRITICAL_SECTION(gen); + retval = gen_throw_lock_held((PyGenObject *)gen, 0, /* Do not close generator when PyExc_GeneratorExit is passed */ typ, val, tb); + Py_END_CRITICAL_SECTION(); retval = async_gen_unwrap_value(o->agt_gen, retval); } if (retval == NULL) { @@ -2217,10 +2334,20 @@ async_gen_athrow_send(PyObject *self, PyObject *arg) return NULL; } +static PyObject * +async_gen_athrow_send(PyObject *self, PyObject *arg) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); + res = async_gen_athrow_send_lock_held(self, arg); + Py_END_CRITICAL_SECTION(); + return res; +} static PyObject * -async_gen_athrow_throw(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +async_gen_athrow_throw_lock_held(PyObject *self, PyObject *const *args, Py_ssize_t nargs) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); PyAsyncGenAThrow *o = _PyAsyncGenAThrow_CAST(self); if (o->agt_state == AWAITABLE_STATE_CLOSED) { @@ -2287,6 +2414,15 @@ async_gen_athrow_throw(PyObject *self, PyObject *const *args, Py_ssize_t nargs) } } +static PyObject * +async_gen_athrow_throw(PyObject *self, PyObject *const *args, Py_ssize_t nargs) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); + res = async_gen_athrow_throw_lock_held(self, args, nargs); + Py_END_CRITICAL_SECTION(); + return res; +} static PyObject * async_gen_athrow_iternext(PyObject *agt) @@ -2296,8 +2432,9 @@ async_gen_athrow_iternext(PyObject *agt) static PyObject * -async_gen_athrow_close(PyObject *self, PyObject *args) +async_gen_athrow_close_lock_held(PyObject *self, PyObject *args) { + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self); PyAsyncGenAThrow *agt = _PyAsyncGenAThrow_CAST(self); if (agt->agt_state == AWAITABLE_STATE_CLOSED) { Py_RETURN_NONE; @@ -2320,6 +2457,15 @@ async_gen_athrow_close(PyObject *self, PyObject *args) } } +static PyObject * +async_gen_athrow_close(PyObject *self, PyObject *args) +{ + PyObject *res; + Py_BEGIN_CRITICAL_SECTION(self); + res = async_gen_athrow_close_lock_held(self, args); + Py_END_CRITICAL_SECTION(); + return res; +} static void async_gen_athrow_finalize(PyAsyncGenAThrow *o) diff --git a/Python/bytecodes.c b/Python/bytecodes.c index 63cf1978e8abe5..db47b51cceace0 100644 --- a/Python/bytecodes.c +++ b/Python/bytecodes.c @@ -1135,20 +1135,25 @@ dummy_func( PyObject *retval_o; assert(frame != &entry_frame); if ((tstate->interp->eval_frame == NULL) && - (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + (Py_TYPE(receiver_o) == &PyGen_Type || + Py_TYPE(receiver_o) == &PyCoro_Type)) { + _PyInterpreterFrame *gen_frame; PyGenObject *gen = (PyGenObject *)receiver_o; - _PyInterpreterFrame *gen_frame = &gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg); - assert(gen_frame->previous == NULL); - gen_frame->previous = frame; + Py_BEGIN_CRITICAL_SECTION(gen); + if (gen->gi_frame_state < FRAME_EXECUTING) { + gen_frame = &gen->gi_iframe; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg); + assert(gen_frame->previous == NULL); + gen_frame->previous = frame; + } + Py_END_CRITICAL_SECTION(); DISPATCH_INLINED(gen_frame); } if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) { @@ -1182,6 +1187,7 @@ dummy_func( op(_SEND_GEN_FRAME, (receiver, v -- receiver, gen_frame: _PyInterpreterFrame *)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); + Py_BEGIN_CRITICAL_SECTION(gen); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); STAT_INC(SEND, hit); @@ -1194,6 +1200,7 @@ dummy_func( assert(INSTRUCTION_SIZE + oparg <= UINT16_MAX); frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg); gen_frame->previous = frame; + Py_END_CRITICAL_SECTION(); } macro(SEND_GEN) = @@ -1211,6 +1218,7 @@ dummy_func( #endif frame->instr_ptr++; PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + Py_BEGIN_CRITICAL_SECTION(gen); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; @@ -1236,6 +1244,7 @@ dummy_func( RELOAD_STACK(); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); value = temp; + Py_END_CRITICAL_SECTION(); LLTRACE_RESUME_FRAME(); } @@ -3133,6 +3142,7 @@ dummy_func( op(_FOR_ITER_GEN_FRAME, (iter -- iter, gen_frame: _PyInterpreterFrame*)) { PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + Py_BEGIN_CRITICAL_SECTION(gen); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING); STAT_INC(FOR_ITER, hit); @@ -3142,6 +3152,7 @@ dummy_func( gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; gen_frame->previous = frame; + Py_END_CRITICAL_SECTION(); // oparg is the return offset from the next instruction. frame->return_offset = (uint16_t)(INSTRUCTION_SIZE + oparg); } diff --git a/Python/ceval.c b/Python/ceval.c index e92a11b16cec81..0bed630a6a0544 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1741,6 +1741,7 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) { assert(frame->owner == FRAME_OWNED_BY_GENERATOR); PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + Py_BEGIN_CRITICAL_SECTION(gen); gen->gi_frame_state = FRAME_CLEARED; assert(tstate->exc_info == &gen->gi_exc_state); tstate->exc_info = gen->gi_exc_state.previous_item; @@ -1751,6 +1752,7 @@ clear_gen_frame(PyThreadState *tstate, _PyInterpreterFrame * frame) _PyErr_ClearExcState(&gen->gi_exc_state); tstate->c_recursion_remaining++; frame->previous = NULL; + Py_END_CRITICAL_SECTION(); } void diff --git a/Python/executor_cases.c.h b/Python/executor_cases.c.h index 22335021faaa6d..fec538288cd012 100644 --- a/Python/executor_cases.c.h +++ b/Python/executor_cases.c.h @@ -1470,6 +1470,9 @@ v = stack_pointer[-1]; receiver = stack_pointer[-2]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); if (Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -1488,6 +1491,9 @@ frame->return_offset = (uint16_t)( 2 + oparg); gen_frame->previous = frame; stack_pointer[-1].bits = (uintptr_t)gen_frame; + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); break; } @@ -1504,6 +1510,9 @@ #endif frame->instr_ptr++; PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; @@ -1530,10 +1539,13 @@ stack_pointer = _PyFrame_GetStackPointer(frame); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); value = temp; - LLTRACE_RESUME_FRAME(); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); + LLTRACE_RESUME_FRAME(); break; } @@ -3740,6 +3752,9 @@ oparg = CURRENT_OPARG(); iter = stack_pointer[-1]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); if (Py_TYPE(gen) != &PyGen_Type) { UOP_STAT_INC(uopcode, miss); JUMP_TO_JUMP_TARGET(); @@ -3755,11 +3770,14 @@ gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; gen_frame->previous = frame; - // oparg is the return offset from the next instruction. - frame->return_offset = (uint16_t)( 2 + oparg); stack_pointer[0].bits = (uintptr_t)gen_frame; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); + // oparg is the return offset from the next instruction. + frame->return_offset = (uint16_t)( 2 + oparg); break; } diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h index bed16b60b76a2f..660c9d804a9da2 100644 --- a/Python/generated_cases.c.h +++ b/Python/generated_cases.c.h @@ -3985,6 +3985,9 @@ { iter = stack_pointer[-1]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(iter); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type, FOR_ITER); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, FOR_ITER); STAT_INC(FOR_ITER, hit); @@ -3994,6 +3997,12 @@ gen->gi_exc_state.previous_item = tstate->exc_info; tstate->exc_info = &gen->gi_exc_state; gen_frame->previous = frame; + stack_pointer[0].bits = (uintptr_t)gen_frame; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); // oparg is the return offset from the next instruction. frame->return_offset = (uint16_t)( 2 + oparg); } @@ -4004,6 +4013,8 @@ // Eventually this should be the only occurrence of this code. assert(tstate->interp->eval_frame == NULL); _PyInterpreterFrame *temp = new_frame; + stack_pointer += -1; + assert(WITHIN_STACK_BOUNDS()); _PyFrame_SetStackPointer(frame, stack_pointer); assert(new_frame->previous == frame || new_frame->previous->previous == frame); CALL_STAT_INC(inlined_py_calls); @@ -4986,6 +4997,9 @@ #endif frame->instr_ptr++; PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; @@ -5012,11 +5026,14 @@ stack_pointer = _PyFrame_GetStackPointer(frame); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); value = temp; + stack_pointer[0] = value; + stack_pointer += 1; + assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); LLTRACE_RESUME_FRAME(); } - stack_pointer[0] = value; - stack_pointer += 1; - assert(WITHIN_STACK_BOUNDS()); DISPATCH(); } @@ -7102,20 +7119,29 @@ PyObject *retval_o; assert(frame != &entry_frame); if ((tstate->interp->eval_frame == NULL) && - (Py_TYPE(receiver_o) == &PyGen_Type || Py_TYPE(receiver_o) == &PyCoro_Type) && - ((PyGenObject *)receiver_o)->gi_frame_state < FRAME_EXECUTING) + (Py_TYPE(receiver_o) == &PyGen_Type || + Py_TYPE(receiver_o) == &PyCoro_Type)) { + _PyInterpreterFrame *gen_frame; PyGenObject *gen = (PyGenObject *)receiver_o; - _PyInterpreterFrame *gen_frame = &gen->gi_iframe; - STACK_SHRINK(1); - _PyFrame_StackPush(gen_frame, v); - gen->gi_frame_state = FRAME_EXECUTING; - gen->gi_exc_state.previous_item = tstate->exc_info; - tstate->exc_info = &gen->gi_exc_state; - assert( 2 + oparg <= UINT16_MAX); - frame->return_offset = (uint16_t)( 2 + oparg); - assert(gen_frame->previous == NULL); - gen_frame->previous = frame; + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); + if (gen->gi_frame_state < FRAME_EXECUTING) { + gen_frame = &gen->gi_iframe; + STACK_SHRINK(1); + _PyFrame_StackPush(gen_frame, v); + gen->gi_frame_state = FRAME_EXECUTING; + gen->gi_exc_state.previous_item = tstate->exc_info; + tstate->exc_info = &gen->gi_exc_state; + assert( 2 + oparg <= UINT16_MAX); + frame->return_offset = (uint16_t)( 2 + oparg); + assert(gen_frame->previous == NULL); + gen_frame->previous = frame; + } + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); DISPATCH_INLINED(gen_frame); } if (PyStackRef_IsNone(v) && PyIter_Check(receiver_o)) { @@ -7177,6 +7203,9 @@ v = stack_pointer[-1]; receiver = stack_pointer[-2]; PyGenObject *gen = (PyGenObject *)PyStackRef_AsPyObjectBorrow(receiver); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); DEOPT_IF(Py_TYPE(gen) != &PyGen_Type && Py_TYPE(gen) != &PyCoro_Type, SEND); DEOPT_IF(gen->gi_frame_state >= FRAME_EXECUTING, SEND); STAT_INC(SEND, hit); @@ -7188,6 +7217,10 @@ assert( 2 + oparg <= UINT16_MAX); frame->return_offset = (uint16_t)( 2 + oparg); gen_frame->previous = frame; + stack_pointer[-1].bits = (uintptr_t)gen_frame; + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); } // _PUSH_FRAME { @@ -8215,6 +8248,9 @@ #endif frame->instr_ptr++; PyGenObject *gen = _PyGen_GetGeneratorFromFrame(frame); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_BEGIN_CRITICAL_SECTION(gen); + stack_pointer = _PyFrame_GetStackPointer(frame); assert(FRAME_SUSPENDED_YIELD_FROM == FRAME_SUSPENDED + 1); assert(oparg == 0 || oparg == 1); gen->gi_frame_state = FRAME_SUSPENDED + oparg; @@ -8241,10 +8277,13 @@ stack_pointer = _PyFrame_GetStackPointer(frame); LOAD_IP(1 + INLINE_CACHE_ENTRIES_SEND); value = temp; - LLTRACE_RESUME_FRAME(); stack_pointer[0] = value; stack_pointer += 1; assert(WITHIN_STACK_BOUNDS()); + _PyFrame_SetStackPointer(frame, stack_pointer); + Py_END_CRITICAL_SECTION(); + stack_pointer = _PyFrame_GetStackPointer(frame); + LLTRACE_RESUME_FRAME(); DISPATCH(); } #undef TIER_ONE