diff options
author | Mark Shannon <mark@hotpy.org> | 2021-12-06 10:13:49 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-12-06 10:13:49 (GMT) |
commit | 299483c95d601ddcfdce2f96418b6499c1fc7b9f (patch) | |
tree | cb367a04d11b54d312305996bbc1039e1a47c929 | |
parent | f34d181fa15e1f082140a4e4a8fa3b77118f8e98 (diff) | |
download | cpython-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.
-rw-r--r-- | Include/cpython/genobject.h | 23 | ||||
-rw-r--r-- | Include/internal/pycore_ceval.h | 2 | ||||
-rw-r--r-- | Include/internal/pycore_frame.h | 2 | ||||
-rw-r--r-- | Lib/test/test_sys.py | 2 | ||||
-rw-r--r-- | Objects/genobject.c | 183 | ||||
-rw-r--r-- | Python/ceval.c | 41 | ||||
-rw-r--r-- | Python/frame.c | 18 |
7 files changed, 127 insertions, 144 deletions
diff --git a/Include/cpython/genobject.h b/Include/cpython/genobject.h index 8f87cf5..ad2818e 100644 --- a/Include/cpython/genobject.h +++ b/Include/cpython/genobject.h @@ -14,7 +14,6 @@ extern "C" { #define _PyGenObject_HEAD(prefix) \ PyObject_HEAD \ /* Note: gi_frame can be NULL if the generator is "finished" */ \ - struct _interpreter_frame *prefix##_xframe; \ /* The code object backing the generator */ \ PyCodeObject *prefix##_code; \ /* List of weak reference. */ \ @@ -23,7 +22,14 @@ extern "C" { PyObject *prefix##_name; \ /* Qualified name of the generator. */ \ 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 { /* The gi_ prefix is intended to remind of generator-iterator. */ @@ -48,7 +54,6 @@ PyAPI_FUNC(void) _PyGen_Finalize(PyObject *self); typedef struct { _PyGenObject_HEAD(cr) - PyObject *cr_origin; } PyCoroObject; PyAPI_DATA(PyTypeObject) PyCoro_Type; @@ -64,18 +69,6 @@ PyAPI_FUNC(PyObject *) PyCoro_New(PyFrameObject *, typedef struct { _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; PyAPI_DATA(PyTypeObject) PyAsyncGen_Type; diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h index 9987f20..26d5677 100644 --- a/Include/internal/pycore_ceval.h +++ b/Include/internal/pycore_ceval.h @@ -113,7 +113,7 @@ static inline void _Py_LeaveRecursiveCall_inline(void) { struct _interpreter_frame *_PyEval_GetFrame(void); -PyObject *_Py_MakeCoro(PyFunctionObject *func, struct _interpreter_frame *); +PyObject *_Py_MakeCoro(PyFunctionObject *func); #ifdef __cplusplus } diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index b0e51a6..f4f7ab9 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -74,7 +74,7 @@ static inline void _PyFrame_StackPush(InterpreterFrame *f, PyObject *value) { #define FRAME_SPECIALS_SIZE ((sizeof(InterpreterFrame)-1)/sizeof(PyObject *)) -InterpreterFrame *_PyFrame_Copy(InterpreterFrame *frame); +void _PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest); static inline void _PyFrame_InitializeSpecials( diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index 2b1ba24..8180289 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -1340,7 +1340,7 @@ class SizeofTest(unittest.TestCase): check(bar, size('PP')) # generator def get_gen(): yield 1 - check(get_gen(), size('P2PPP4P')) + check(get_gen(), size('P2PPP4P4c8P2iciP')) # iterator check(iter('abc'), size('lP')) # callable-iterator 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; diff --git a/Python/ceval.c b/Python/ceval.c index 05897c5..a8bbad3 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -5854,8 +5854,8 @@ fail_post_args: return -1; } -static InterpreterFrame * -make_coro_frame(PyThreadState *tstate, +static int +initialize_coro_frame(InterpreterFrame *frame, PyThreadState *tstate, PyFunctionObject *func, PyObject *locals, PyObject *const *args, Py_ssize_t argcount, PyObject *kwnames) @@ -5863,37 +5863,15 @@ make_coro_frame(PyThreadState *tstate, assert(is_tstate_valid(tstate)); assert(func->func_defaults == NULL || PyTuple_CheckExact(func->func_defaults)); 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); for (int i = 0; i < code->co_nlocalsplus; i++) { frame->localsplus[i] = NULL; } assert(frame->frame_obj == NULL); - if (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; + return initialize_locals(tstate, func, frame->localsplus, args, argcount, kwnames); } + /* Consumes all the references to the args */ static PyObject * make_coro(PyThreadState *tstate, PyFunctionObject *func, @@ -5902,14 +5880,17 @@ make_coro(PyThreadState *tstate, PyFunctionObject *func, PyObject *kwnames) { 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); - if (frame == NULL) { + PyObject *gen = _Py_MakeCoro(func); + if (gen == NULL) { return NULL; } - PyObject *gen = _Py_MakeCoro(func, frame); - if (gen == 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; } diff --git a/Python/frame.c b/Python/frame.c index 21f2ced..da2c1c4 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -43,18 +43,12 @@ _PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame) return f; } -InterpreterFrame * -_PyFrame_Copy(InterpreterFrame *frame) +void +_PyFrame_Copy(InterpreterFrame *src, InterpreterFrame *dest) { - assert(frame->stacktop >= frame->f_code->co_nlocalsplus); - Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; - InterpreterFrame *copy = PyMem_Malloc(size); - if (copy == NULL) { - PyErr_NoMemory(); - return NULL; - } - memcpy(copy, frame, size); - return copy; + assert(src->stacktop >= src->f_code->co_nlocalsplus); + Py_ssize_t size = ((char*)&src->localsplus[src->stacktop]) - (char *)src; + memcpy(dest, src, size); } static inline void @@ -112,7 +106,7 @@ _PyFrame_Clear(InterpreterFrame * frame) } Py_DECREF(f); } - assert(_PyFrame_GetStackPointer(frame) >= _PyFrame_Stackbase(frame)); + assert(frame->stacktop >= 0); for (int i = 0; i < frame->stacktop; i++) { Py_XDECREF(frame->localsplus[i]); } |