summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorMark Shannon <mark@hotpy.org>2021-12-06 10:13:49 (GMT)
committerGitHub <noreply@github.com>2021-12-06 10:13:49 (GMT)
commit299483c95d601ddcfdce2f96418b6499c1fc7b9f (patch)
treecb367a04d11b54d312305996bbc1039e1a47c929 /Objects
parentf34d181fa15e1f082140a4e4a8fa3b77118f8e98 (diff)
downloadcpython-299483c95d601ddcfdce2f96418b6499c1fc7b9f.zip
cpython-299483c95d601ddcfdce2f96418b6499c1fc7b9f.tar.gz
cpython-299483c95d601ddcfdce2f96418b6499c1fc7b9f.tar.bz2
bpo-45963: Make space for the InterpreterFrame of a generator in that generator. (GH-29891)
* Make generator, coroutine and async gen structs all the same size. * Store interpreter frame in generator (and coroutine). Reduces the number of allocations neeeded for a generator from two to one.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/genobject.c183
1 files changed, 99 insertions, 84 deletions
diff --git a/Objects/genobject.c b/Objects/genobject.c
index 04d98a2..c4ba660 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -36,8 +36,8 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
Py_VISIT(gen->gi_code);
Py_VISIT(gen->gi_name);
Py_VISIT(gen->gi_qualname);
- InterpreterFrame *frame = gen->gi_xframe;
- if (frame != NULL) {
+ if (gen->gi_frame_valid) {
+ InterpreterFrame *frame = (InterpreterFrame *)(gen->gi_iframe);
assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0);
int err = _PyFrame_Traverse(frame, visit, arg);
if (err) {
@@ -56,14 +56,14 @@ _PyGen_Finalize(PyObject *self)
PyObject *res = NULL;
PyObject *error_type, *error_value, *error_traceback;
- if (gen->gi_xframe == NULL || _PyFrameHasCompleted(gen->gi_xframe)) {
+ if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted((InterpreterFrame *)gen->gi_iframe)) {
/* Generator isn't paused, so no need to close */
return;
}
if (PyAsyncGen_CheckExact(self)) {
PyAsyncGenObject *agen = (PyAsyncGenObject*)self;
- PyObject *finalizer = agen->ag_finalizer;
+ PyObject *finalizer = agen->ag_origin_or_finalizer;
if (finalizer && !agen->ag_closed) {
/* Save the current exception, if any. */
PyErr_Fetch(&error_type, &error_value, &error_traceback);
@@ -88,7 +88,7 @@ _PyGen_Finalize(PyObject *self)
issue a RuntimeWarning. */
if (gen->gi_code != NULL &&
((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE &&
- gen->gi_xframe->f_lasti == -1)
+ ((InterpreterFrame *)gen->gi_iframe)->f_lasti == -1)
{
_PyErr_WarnUnawaitedCoroutine((PyObject *)gen);
}
@@ -129,18 +129,17 @@ gen_dealloc(PyGenObject *gen)
/* We have to handle this case for asynchronous generators
right here, because this code has to be between UNTRACK
and GC_Del. */
- Py_CLEAR(((PyAsyncGenObject*)gen)->ag_finalizer);
+ Py_CLEAR(((PyAsyncGenObject*)gen)->ag_origin_or_finalizer);
}
- InterpreterFrame *frame = gen->gi_xframe;
- if (frame != NULL) {
- gen->gi_xframe = NULL;
+ if (gen->gi_frame_valid) {
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+ gen->gi_frame_valid = 0;
frame->generator = NULL;
frame->previous = NULL;
_PyFrame_Clear(frame);
- PyMem_Free(frame);
}
if (((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE) {
- Py_CLEAR(((PyCoroObject *)gen)->cr_origin);
+ Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
}
Py_CLEAR(gen->gi_code);
Py_CLEAR(gen->gi_name);
@@ -154,11 +153,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
int exc, int closing)
{
PyThreadState *tstate = _PyThreadState_GET();
- InterpreterFrame *frame = gen->gi_xframe;
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
PyObject *result;
*presult = NULL;
- if (frame != NULL && _PyFrame_IsExecuting(frame)) {
+ if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) {
const char *msg = "generator already executing";
if (PyCoro_CheckExact(gen)) {
msg = "coroutine already executing";
@@ -169,7 +168,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
PyErr_SetString(PyExc_ValueError, msg);
return PYGEN_ERROR;
}
- if (frame == NULL || _PyFrameHasCompleted(frame)) {
+ if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) {
if (PyCoro_CheckExact(gen) && !closing) {
/* `gen` is an exhausted coroutine: raise an error,
except when called from gen_close(), which should
@@ -188,6 +187,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
return PYGEN_ERROR;
}
+ assert(gen->gi_frame_valid);
assert(_PyFrame_IsRunnable(frame));
/* Push arg onto the frame's value stack */
result = arg ? arg : Py_None;
@@ -254,9 +254,8 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult,
_PyErr_ClearExcState(&gen->gi_exc_state);
frame->generator = NULL;
- gen->gi_xframe = NULL;
+ gen->gi_frame_valid = 0;
_PyFrame_Clear(frame);
- PyMem_Free(frame);
*presult = result;
return result ? PYGEN_RETURN : PYGEN_ERROR;
}
@@ -337,8 +336,8 @@ _PyGen_yf(PyGenObject *gen)
{
PyObject *yf = NULL;
- if (gen->gi_xframe) {
- InterpreterFrame *frame = gen->gi_xframe;
+ if (gen->gi_frame_valid) {
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
PyObject *bytecode = gen->gi_code->co_code;
unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode);
@@ -367,10 +366,11 @@ gen_close(PyGenObject *gen, PyObject *args)
int err = 0;
if (yf) {
- PyFrameState state = gen->gi_xframe->f_state;
- gen->gi_xframe->f_state = FRAME_EXECUTING;
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+ PyFrameState state = frame->f_state;
+ frame->f_state = FRAME_EXECUTING;
err = gen_close_iter(yf);
- gen->gi_xframe->f_state = state;
+ frame->f_state = state;
Py_DECREF(yf);
}
if (err == 0)
@@ -408,6 +408,7 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
_Py_IDENTIFIER(throw);
if (yf) {
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
PyObject *ret;
int err;
if (PyErr_GivenExceptionMatches(typ, PyExc_GeneratorExit) &&
@@ -417,10 +418,10 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
We have to allow some awaits to work it through, hence the
`close_on_genexit` parameter here.
*/
- PyFrameState state = gen->gi_xframe->f_state;
- gen->gi_xframe->f_state = FRAME_EXECUTING;
+ PyFrameState state = frame->f_state;
+ frame->f_state = FRAME_EXECUTING;
err = gen_close_iter(yf);
- gen->gi_xframe->f_state = state;
+ frame->f_state = state;
Py_DECREF(yf);
if (err < 0)
return gen_send_ex(gen, Py_None, 1, 0);
@@ -429,9 +430,6 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
if (PyGen_CheckExact(yf) || PyCoro_CheckExact(yf)) {
/* `yf` is a generator or a coroutine. */
PyThreadState *tstate = _PyThreadState_GET();
- InterpreterFrame *frame = gen->gi_xframe;
-
-
/* Since we are fast-tracking things by skipping the eval loop,
we need to update the current frame so the stack trace
will be reported correctly to the user. */
@@ -442,11 +440,11 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
tstate->cframe->current_frame = frame;
/* Close the generator that we are currently iterating with
'yield from' or awaiting on with 'await'. */
- PyFrameState state = gen->gi_xframe->f_state;
- gen->gi_xframe->f_state = FRAME_EXECUTING;
+ PyFrameState state = frame->f_state;
+ frame->f_state = FRAME_EXECUTING;
ret = _gen_throw((PyGenObject *)yf, close_on_genexit,
typ, val, tb);
- gen->gi_xframe->f_state = state;
+ frame->f_state = state;
tstate->cframe->current_frame = prev;
frame->previous = NULL;
} else {
@@ -460,22 +458,23 @@ _gen_throw(PyGenObject *gen, int close_on_genexit,
Py_DECREF(yf);
goto throw_here;
}
- PyFrameState state = gen->gi_xframe->f_state;
- gen->gi_xframe->f_state = FRAME_EXECUTING;
+ PyFrameState state = frame->f_state;
+ frame->f_state = FRAME_EXECUTING;
ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL);
- gen->gi_xframe->f_state = state;
+ frame->f_state = state;
Py_DECREF(meth);
}
Py_DECREF(yf);
if (!ret) {
PyObject *val;
/* Pop subiterator from stack */
- ret = _PyFrame_StackPop(gen->gi_xframe);
+ assert(gen->gi_frame_valid);
+ ret = _PyFrame_StackPop((InterpreterFrame *)gen->gi_iframe);
assert(ret == yf);
Py_DECREF(ret);
/* Termination repetition of YIELD_FROM */
- assert(gen->gi_xframe->f_lasti >= 0);
- gen->gi_xframe->f_lasti += 1;
+ assert(frame->f_lasti >= 0);
+ frame->f_lasti += 1;
if (_PyGen_FetchStopIterationValue(&val) == 0) {
ret = gen_send(gen, val);
Py_DECREF(val);
@@ -732,10 +731,10 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored))
static PyObject *
gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored))
{
- if (gen->gi_xframe == NULL) {
+ if (gen->gi_frame_valid == 0) {
Py_RETURN_FALSE;
}
- return PyBool_FromLong(_PyFrame_IsExecuting(gen->gi_xframe));
+ return PyBool_FromLong(_PyFrame_IsExecuting((InterpreterFrame *)gen->gi_iframe));
}
static PyObject *
@@ -744,10 +743,10 @@ _gen_getframe(PyGenObject *gen, const char *const name)
if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) {
return NULL;
}
- if (gen->gi_xframe == NULL) {
+ if (gen->gi_frame_valid == 0) {
Py_RETURN_NONE;
}
- return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject(gen->gi_xframe));
+ return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((InterpreterFrame *)gen->gi_iframe));
}
static PyObject *
@@ -773,10 +772,24 @@ static PyMemberDef gen_memberlist[] = {
{NULL} /* Sentinel */
};
+static PyObject *
+gen_sizeof(PyGenObject *gen, PyObject *Py_UNUSED(ignored))
+{
+ Py_ssize_t res;
+ res = offsetof(PyGenObject, gi_iframe) + offsetof(InterpreterFrame, localsplus);
+ PyCodeObject *code = gen->gi_code;
+ res += (code->co_nlocalsplus+code->co_stacksize) * sizeof(PyObject *);
+ return PyLong_FromSsize_t(res);
+}
+
+PyDoc_STRVAR(sizeof__doc__,
+"gen.__sizeof__() -> size of gen in memory, in bytes");
+
static PyMethodDef gen_methods[] = {
{"send",(PyCFunction)gen_send, METH_O, send_doc},
{"throw",(PyCFunction)gen_throw, METH_VARARGS, throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, close_doc},
+ {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
{NULL, NULL} /* Sentinel */
};
@@ -791,8 +804,9 @@ static PyAsyncMethods gen_as_async = {
PyTypeObject PyGen_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"generator", /* tp_name */
- sizeof(PyGenObject), /* tp_basicsize */
- 0, /* tp_itemsize */
+ offsetof(PyGenObject, gi_iframe) +
+ offsetof(InterpreterFrame, localsplus), /* tp_basicsize */
+ sizeof(PyObject *), /* tp_itemsize */
/* methods */
(destructor)gen_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
@@ -842,18 +856,16 @@ PyTypeObject PyGen_Type = {
};
static PyObject *
-make_gen(PyTypeObject *type, PyFunctionObject *func, InterpreterFrame *frame)
+make_gen(PyTypeObject *type, PyFunctionObject *func)
{
- PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
+ PyCodeObject *code = (PyCodeObject *)func->func_code;
+ int slots = code->co_nlocalsplus + code->co_stacksize;
+ PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, slots);
if (gen == NULL) {
- assert(frame->frame_obj == NULL);
- _PyFrame_Clear(frame);
- PyMem_Free(frame);
return NULL;
}
- gen->gi_xframe = frame;
- frame->generator = (PyObject *)gen;
- gen->gi_code = frame->f_code;
+ gen->gi_frame_valid = 0;
+ gen->gi_code = (PyCodeObject *)func->func_code;
Py_INCREF(gen->gi_code);
gen->gi_weakreflist = NULL;
gen->gi_exc_state.exc_type = NULL;
@@ -878,28 +890,28 @@ static PyObject *
compute_cr_origin(int origin_depth);
PyObject *
-_Py_MakeCoro(PyFunctionObject *func, InterpreterFrame *frame)
+_Py_MakeCoro(PyFunctionObject *func)
{
int coro_flags = ((PyCodeObject *)func->func_code)->co_flags &
(CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR);
assert(coro_flags);
if (coro_flags == CO_GENERATOR) {
- return make_gen(&PyGen_Type, func, frame);
+ return make_gen(&PyGen_Type, func);
}
if (coro_flags == CO_ASYNC_GENERATOR) {
PyAsyncGenObject *o;
- o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, func, frame);
+ o = (PyAsyncGenObject *)make_gen(&PyAsyncGen_Type, func);
if (o == NULL) {
return NULL;
}
- o->ag_finalizer = NULL;
+ o->ag_origin_or_finalizer = NULL;
o->ag_closed = 0;
o->ag_hooks_inited = 0;
o->ag_running_async = 0;
return (PyObject*)o;
}
assert (coro_flags == CO_COROUTINE);
- PyObject *coro = make_gen(&PyCoro_Type, func, frame);
+ PyObject *coro = make_gen(&PyCoro_Type, func);
if (!coro) {
return NULL;
}
@@ -907,16 +919,15 @@ _Py_MakeCoro(PyFunctionObject *func, InterpreterFrame *frame)
int origin_depth = tstate->coroutine_origin_tracking_depth;
if (origin_depth == 0) {
- ((PyCoroObject *)coro)->cr_origin = NULL;
+ ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
} else {
PyObject *cr_origin = compute_cr_origin(origin_depth);
- ((PyCoroObject *)coro)->cr_origin = cr_origin;
+ ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
if (!cr_origin) {
Py_DECREF(coro);
return NULL;
}
}
-
return coro;
}
@@ -924,27 +935,27 @@ static PyObject *
gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f,
PyObject *name, PyObject *qualname)
{
- PyGenObject *gen = PyObject_GC_New(PyGenObject, type);
+ PyCodeObject *code = f->f_frame->f_code;
+ int size = code->co_nlocalsplus + code->co_stacksize;
+ PyGenObject *gen = PyObject_GC_NewVar(PyGenObject, type, size);
if (gen == NULL) {
Py_DECREF(f);
return NULL;
}
-
- /* Take ownership of the frame */
+ /* Copy the frame */
assert(f->f_frame->frame_obj == NULL);
assert(f->f_owns_frame);
- gen->gi_xframe = _PyFrame_Copy((InterpreterFrame *)f->_f_frame_data);
- if (gen->gi_xframe == NULL) {
- Py_DECREF(f);
- Py_DECREF(gen);
- return NULL;
- }
- gen->gi_xframe->frame_obj = f;
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
+ _PyFrame_Copy((InterpreterFrame *)f->_f_frame_data, frame);
+ gen->gi_frame_valid = 1;
+ assert(frame->frame_obj == f);
f->f_owns_frame = 0;
- gen->gi_xframe->generator = (PyObject *) gen;
+ f->f_frame = frame;
+ frame->generator = (PyObject *) gen;
assert(PyObject_GC_IsTracked((PyObject *)f));
-
gen->gi_code = PyFrame_GetCode(f);
+ Py_INCREF(gen->gi_code);
+ Py_DECREF(f);
gen->gi_weakreflist = NULL;
gen->gi_exc_state.exc_type = NULL;
gen->gi_exc_state.exc_value = NULL;
@@ -1077,10 +1088,10 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored))
static PyObject *
cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored))
{
- if (coro->cr_xframe == NULL) {
+ if (coro->cr_frame_valid == 0) {
Py_RETURN_FALSE;
}
- return PyBool_FromLong(_PyFrame_IsExecuting(coro->cr_xframe));
+ return PyBool_FromLong(_PyFrame_IsExecuting((InterpreterFrame *)coro->cr_iframe));
}
static PyObject *
@@ -1104,7 +1115,7 @@ static PyGetSetDef coro_getsetlist[] = {
static PyMemberDef coro_memberlist[] = {
{"cr_code", T_OBJECT, offsetof(PyCoroObject, cr_code), READONLY|PY_AUDIT_READ},
- {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin), READONLY},
+ {"cr_origin", T_OBJECT, offsetof(PyCoroObject, cr_origin_or_finalizer), READONLY},
{NULL} /* Sentinel */
};
@@ -1123,6 +1134,7 @@ static PyMethodDef coro_methods[] = {
{"send",(PyCFunction)gen_send, METH_O, coro_send_doc},
{"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc},
+ {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
{NULL, NULL} /* Sentinel */
};
@@ -1136,8 +1148,9 @@ static PyAsyncMethods coro_as_async = {
PyTypeObject PyCoro_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"coroutine", /* tp_name */
- sizeof(PyCoroObject), /* tp_basicsize */
- 0, /* tp_itemsize */
+ offsetof(PyCoroObject, cr_iframe) +
+ offsetof(InterpreterFrame, localsplus), /* tp_basicsize */
+ sizeof(PyObject *), /* tp_itemsize */
/* methods */
(destructor)gen_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
@@ -1318,10 +1331,10 @@ PyCoro_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
int origin_depth = tstate->coroutine_origin_tracking_depth;
if (origin_depth == 0) {
- ((PyCoroObject *)coro)->cr_origin = NULL;
+ ((PyCoroObject *)coro)->cr_origin_or_finalizer = NULL;
} else {
PyObject *cr_origin = compute_cr_origin(origin_depth);
- ((PyCoroObject *)coro)->cr_origin = cr_origin;
+ ((PyCoroObject *)coro)->cr_origin_or_finalizer = cr_origin;
if (!cr_origin) {
Py_DECREF(coro);
return NULL;
@@ -1382,7 +1395,7 @@ typedef struct _PyAsyncGenWrappedValue {
static int
async_gen_traverse(PyAsyncGenObject *gen, visitproc visit, void *arg)
{
- Py_VISIT(gen->ag_finalizer);
+ Py_VISIT(gen->ag_origin_or_finalizer);
return gen_traverse((PyGenObject*)gen, visit, arg);
}
@@ -1413,7 +1426,7 @@ async_gen_init_hooks(PyAsyncGenObject *o)
finalizer = tstate->async_gen_finalizer;
if (finalizer) {
Py_INCREF(finalizer);
- o->ag_finalizer = finalizer;
+ o->ag_origin_or_finalizer = finalizer;
}
firstiter = tstate->async_gen_firstiter;
@@ -1508,6 +1521,7 @@ static PyMethodDef async_gen_methods[] = {
{"asend", (PyCFunction)async_gen_asend, METH_O, async_asend_doc},
{"athrow",(PyCFunction)async_gen_athrow, METH_VARARGS, async_athrow_doc},
{"aclose", (PyCFunction)async_gen_aclose, METH_NOARGS, async_aclose_doc},
+ {"__sizeof__", (PyCFunction)gen_sizeof, METH_NOARGS, sizeof__doc__},
{"__class_getitem__", Py_GenericAlias,
METH_O|METH_CLASS, PyDoc_STR("See PEP 585")},
{NULL, NULL} /* Sentinel */
@@ -1525,8 +1539,9 @@ static PyAsyncMethods async_gen_as_async = {
PyTypeObject PyAsyncGen_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"async_generator", /* tp_name */
- sizeof(PyAsyncGenObject), /* tp_basicsize */
- 0, /* tp_itemsize */
+ offsetof(PyAsyncGenObject, ag_iframe) +
+ offsetof(InterpreterFrame, localsplus), /* tp_basicsize */
+ sizeof(PyObject *), /* tp_itemsize */
/* methods */
(destructor)gen_dealloc, /* tp_dealloc */
0, /* tp_vectorcall_offset */
@@ -1594,7 +1609,7 @@ PyAsyncGen_New(PyFrameObject *f, PyObject *name, PyObject *qualname)
if (o == NULL) {
return NULL;
}
- o->ag_finalizer = NULL;
+ o->ag_origin_or_finalizer = NULL;
o->ag_closed = 0;
o->ag_hooks_inited = 0;
o->ag_running_async = 0;
@@ -2011,7 +2026,7 @@ static PyObject *
async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
{
PyGenObject *gen = (PyGenObject*)o->agt_gen;
- InterpreterFrame *frame = gen->gi_xframe;
+ InterpreterFrame *frame = (InterpreterFrame *)gen->gi_iframe;
PyObject *retval;
if (o->agt_state == AWAITABLE_STATE_CLOSED) {
@@ -2021,7 +2036,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg)
return NULL;
}
- if (frame == NULL || _PyFrameHasCompleted(frame)) {
+ if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) {
o->agt_state = AWAITABLE_STATE_CLOSED;
PyErr_SetNone(PyExc_StopIteration);
return NULL;