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.
This commit is contained in:
Mark Shannon 2021-12-06 10:13:49 +00:00 committed by GitHub
parent f34d181fa1
commit 299483c95d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 128 additions and 145 deletions

View File

@ -14,7 +14,6 @@ extern "C" {
#define _PyGenObject_HEAD(prefix) \ #define _PyGenObject_HEAD(prefix) \
PyObject_HEAD \ PyObject_HEAD \
/* Note: gi_frame can be NULL if the generator is "finished" */ \ /* Note: gi_frame can be NULL if the generator is "finished" */ \
struct _interpreter_frame *prefix##_xframe; \
/* The code object backing the generator */ \ /* The code object backing the generator */ \
PyCodeObject *prefix##_code; \ PyCodeObject *prefix##_code; \
/* List of weak reference. */ \ /* List of weak reference. */ \
@ -23,7 +22,14 @@ extern "C" {
PyObject *prefix##_name; \ PyObject *prefix##_name; \
/* Qualified name of the generator. */ \ /* Qualified name of the generator. */ \
PyObject *prefix##_qualname; \ PyObject *prefix##_qualname; \
_PyErr_StackItem prefix##_exc_state; _PyErr_StackItem prefix##_exc_state; \
PyObject *prefix##_origin_or_finalizer; \
char prefix##_hooks_inited; \
char prefix##_closed; \
char prefix##_running_async; \
/* The frame */ \
char prefix##_frame_valid; \
PyObject *prefix##_iframe[1];
typedef struct { typedef struct {
/* The gi_ prefix is intended to remind of generator-iterator. */ /* The gi_ prefix is intended to remind of generator-iterator. */
@ -48,7 +54,6 @@ PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self);
typedef struct { typedef struct {
_PyGenObject_HEAD(cr) _PyGenObject_HEAD(cr)
PyObject *cr_origin;
} PyCoroObject; } PyCoroObject;
PyAPI_DATA(PyTypeObject) PyCoro_Type; PyAPI_DATA(PyTypeObject) PyCoro_Type;
@ -64,18 +69,6 @@ PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *,
typedef struct { typedef struct {
_PyGenObject_HEAD(ag) _PyGenObject_HEAD(ag)
PyObject *ag_finalizer;
/* Flag is set to 1 when hooks set up by sys.set_asyncgen_hooks
were called on the generator, to avoid calling them more
than once. */
int ag_hooks_inited;
/* Flag is set to 1 when aclose() is called for the first time, or
when a StopAsyncIteration exception is raised. */
int ag_closed;
int ag_running_async;
} PyAsyncGenObject; } PyAsyncGenObject;
PyAPI_DATA(PyTypeObject) PyAsyncGen_Type; PyAPI_DATA(PyTypeObject) PyAsyncGen_Type;

View File

@ -113,7 +113,7 @@ static inline void _Py_LeaveRecursiveCall_inline(void) {
struct _interpreter_frame *_PyEval_GetFrame(void); struct _interpreter_frame *_PyEval_GetFrame(void);
PyObject *_Py_MakeCoro(PyFunctionObject *func, struct _interpreter_frame *); PyObject *_Py_MakeCoro(PyFunctionObject *func);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -74,7 +74,7 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) {
#define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *))
InterpreterFrame *_PyFrame_Copy(InterpreterFrame *frame); void _PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest);
static inline void static inline void
_PyFrame_InitializeSpecials( _PyFrame_InitializeSpecials(

View File

@ -1340,7 +1340,7 @@ class SizeofTest(unittest.TestCase):
check(bar, size('PP')) check(bar, size('PP'))
# generator # generator
def get_gen(): yield 1 def get_gen(): yield 1
check(get_gen(), size('P2PPP4P')) check(get_gen(), size('P2PPP4P4c8P2iciP'))
# iterator # iterator
check(iter('abc'), size('lP')) check(iter('abc'), size('lP'))
# callable-iterator # callable-iterator

View File

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

View File

@ -5854,8 +5854,8 @@ fail_post_args:
return -1; return -1;
} }
static InterpreterFrame * static int
make_coro_frame(PyThreadState *tstate, initialize_coro_frame(InterpreterFrame *frame, PyThreadState *tstate,
PyFunctionObject *func, PyObject *locals, PyFunctionObject *func, PyObject *locals,
PyObject *const *args, Py_ssize_t argcount, PyObject *const *args, Py_ssize_t argcount,
PyObject *kwnames) PyObject *kwnames)
@ -5863,37 +5863,15 @@ make_coro_frame(PyThreadState *tstate,
assert(is_tstate_valid(tstate)); assert(is_tstate_valid(tstate));
assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults)); assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults));
PyCodeObject *code = (PyCodeObject *)func->func_code; PyCodeObject *code = (PyCodeObject *)func->func_code;
int size = code->co_nlocalsplus+code->co_stacksize + FRAME_SPECIALS_SIZE;
InterpreterFrame *frame = (InterpreterFrame *)PyMem_Malloc(sizeof(PyObject *)*size);
if (frame == NULL) {
goto fail_no_memory;
}
_PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus); _PyFrame_InitializeSpecials(frame, func, locals, code->co_nlocalsplus);
for (int i = 0; i < code->co_nlocalsplus; i++) { for (int i = 0; i < code->co_nlocalsplus; i++) {
frame->localsplus[i] = NULL; frame->localsplus[i] = NULL;
} }
assert(frame->frame_obj == NULL); assert(frame->frame_obj == NULL);
if (initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames)) { return initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames);
_PyFrame_Clear(frame);
PyMem_Free(frame);
return NULL;
}
return frame;
fail_no_memory:
/* Consume the references */
for (Py_ssize_t i = 0; i < argcount; i++) {
Py_DECREF(args[i]);
}
if (kwnames) {
Py_ssize_t kwcount = PyTuple_GET_SIZE(kwnames);
for (Py_ssize_t i = 0; i < kwcount; i++) {
Py_DECREF(args[i+argcount]);
}
}
PyErr_NoMemory();
return NULL;
} }
/* Consumes all the references to the args */ /* Consumes all the references to the args */
static PyObject * static PyObject *
make_coro(PyThreadState *tstate, PyFunctionObject *func, make_coro(PyThreadState *tstate, PyFunctionObject *func,
@ -5902,14 +5880,17 @@ make_coro(PyThreadState *tstate, PyFunctionObject *func,
PyObject *kwnames) PyObject *kwnames)
{ {
assert (((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR)); assert (((PyCodeObject *)func->func_code)->co_flags & (CO_GENERATOR | CO_COROUTINE | CO_ASYNC_GENERATOR));
InterpreterFrame *frame = make_coro_frame(tstate, func, locals, args, argcount, kwnames); PyObject *gen = _Py_MakeCoro(func);
if (frame == NULL) {
return NULL;
}
PyObject *gen = _Py_MakeCoro(func, frame);
if (gen == NULL) { if (gen == NULL) {
return NULL; return NULL;
} }
InterpreterFrame *frame = (InterpreterFrame *)((PyGenObject *)gen)->gi_iframe;
if (initialize_coro_frame(frame, tstate, func, locals, args, argcount, kwnames)) {
Py_DECREF(gen);
return NULL;
}
frame->generator = gen;
((PyGenObject *)gen)->gi_frame_valid = 1;
return gen; return gen;
} }

View File

@ -43,18 +43,12 @@ _PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame)
return f; return f;
} }
InterpreterFrame * void
_PyFrame_Copy(InterpreterFrame *frame) _PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest)
{ {
assert(frame->stacktop >= frame->f_code->co_nlocalsplus); assert(src->stacktop >= src->f_code->co_nlocalsplus);
Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src;
InterpreterFrame *copy = PyMem_Malloc(size); memcpy(dest, src, size);
if (copy == NULL) {
PyErr_NoMemory();
return NULL;
}
memcpy(copy, frame, size);
return copy;
} }
static inline void static inline void
@ -112,7 +106,7 @@ _PyFrame_Clear(InterpreterFrame * frame)
} }
Py_DECREF(f); Py_DECREF(f);
} }
assert(_PyFrame_GetStackPointer(frame) >= _PyFrame_Stackbase(frame)); assert(frame->stacktop >= 0);
for (int i = 0; i < frame->stacktop; i++) { for (int i = 0; i < frame->stacktop; i++) {
Py_XDECREF(frame->localsplus[i]); Py_XDECREF(frame->localsplus[i]);
} }