From b4cb6bdfdb81da4091a4b5e33cee7284c06af6a0 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 17 Mar 2022 15:52:40 +0000 Subject: [PATCH 01/10] Remove the f_state field from InterpreterFrame and make ownership of the frame explicit. --- Include/cpython/frameobject.h | 1 - Include/cpython/genobject.h | 2 +- Include/cpython/pystate.h | 1 + Include/frameobject.h | 12 ++ Include/internal/pycore_frame.h | 48 +++----- Modules/_xxsubinterpretersmodule.c | 5 +- Objects/frameobject.c | 120 +++++++++++------- Objects/genobject.c | 98 +++++++-------- Python/ceval.c | 189 +++++++++++++++-------------- Python/frame.c | 11 +- 10 files changed, 262 insertions(+), 225 deletions(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index ebaecbed1b487b..e39a4f34435916 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -24,4 +24,3 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); -PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); diff --git a/Include/cpython/genobject.h b/Include/cpython/genobject.h index b485ac6183e2ee..50aea8481839a6 100644 --- a/Include/cpython/genobject.h +++ b/Include/cpython/genobject.h @@ -27,7 +27,7 @@ extern "C" { char prefix##_closed; \ char prefix##_running_async; \ /* The frame */ \ - char prefix##_frame_valid; \ + char prefix##_frame_state; \ PyObject *prefix##_iframe[1]; typedef struct { diff --git a/Include/cpython/pystate.h b/Include/cpython/pystate.h index 26d6f7576e524f..1af21a2c947d99 100644 --- a/Include/cpython/pystate.h +++ b/Include/cpython/pystate.h @@ -103,6 +103,7 @@ struct _ts { This is to prevent the actual trace/profile code from being recorded in the trace/profile. */ int tracing; + int tracing_what; /* The event currently being traced, if any. */ /* Pointer to current _PyCFrame in the C stack frame of the currently, * or most recently, executing _PyEval_EvalFrameDefault. */ diff --git a/Include/frameobject.h b/Include/frameobject.h index adb628f6314fcf..a59d88c14ad121 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -14,6 +14,18 @@ extern "C" { # undef Py_CPYTHON_FRAMEOBJECT_H #endif +typedef enum _framestate { + FRAME_CREATED = -2, + FRAME_SUSPENDED = -1, + FRAME_EXECUTING = 0, + FRAME_COMPLETED = 1, + FRAME_CLEARED = 4 +} PyFrameState; + +PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); +PyAPI_FUNC(PyFrameState) PyFrame_GetState(PyFrameObject *frame); + + #ifdef __cplusplus } #endif diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 207983dcc22d7c..3267eab4fc07ab 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -5,6 +5,7 @@ extern "C" { #endif #include +#include struct _frame { PyObject_HEAD @@ -14,7 +15,6 @@ struct _frame { int f_lineno; /* Current line number. Only valid if non-zero */ char f_trace_lines; /* Emit per-line trace events? */ char f_trace_opcodes; /* Emit per-opcode trace events? */ - char f_owns_frame; /* This frame owns the frame */ /* The frame data, if this frame object owns the frame */ PyObject *_f_frame_data[1]; }; @@ -28,20 +28,13 @@ extern void _PyFrame_Fini(PyInterpreterState *interp); /* other API */ -/* These values are chosen so that the inline functions below all - * compare f_state to zero. - */ -enum _framestate { - FRAME_CREATED = -2, - FRAME_SUSPENDED = -1, - FRAME_EXECUTING = 0, - FRAME_RETURNED = 1, - FRAME_UNWINDING = 2, - FRAME_RAISED = 3, - FRAME_CLEARED = 4 -}; -typedef signed char PyFrameState; +enum _frameowner { + FRAME_OWNED_BY_THREAD = 0, + FRAME_OWNED_BY_GENERATOR = 1, + FRAME_OWNED_BY_FRAME_OBJECT = 2 + /* FRAME_CLEARED is also valid */ +}; /* frame->f_lasti refers to the index of the last instruction, @@ -58,24 +51,11 @@ typedef struct _PyInterpreterFrame { struct _PyInterpreterFrame *previous; int f_lasti; /* Last instruction if called */ int stacktop; /* Offset of TOS from localsplus */ - PyFrameState f_state; /* What state the frame is in */ bool is_entry; // Whether this is the "root" frame for the current _PyCFrame. - bool is_generator; + char owner; PyObject *localsplus[1]; } _PyInterpreterFrame; -static inline int _PyFrame_IsRunnable(_PyInterpreterFrame *f) { - return f->f_state < FRAME_EXECUTING; -} - -static inline int _PyFrame_IsExecuting(_PyInterpreterFrame *f) { - return f->f_state == FRAME_EXECUTING; -} - -static inline int _PyFrameHasCompleted(_PyInterpreterFrame *f) { - return f->f_state > FRAME_EXECUTING; -} - static inline PyObject **_PyFrame_Stackbase(_PyInterpreterFrame *f) { return f->localsplus + f->f_code->co_nlocalsplus; } @@ -115,9 +95,8 @@ _PyFrame_InitializeSpecials( frame->stacktop = nlocalsplus; frame->frame_obj = NULL; frame->f_lasti = -1; - frame->f_state = FRAME_CREATED; frame->is_entry = false; - frame->is_generator = false; + frame->owner = FRAME_OWNED_BY_THREAD; } /* Gets the pointer to the locals array @@ -204,6 +183,15 @@ void _PyThreadState_PopFrame(PyThreadState *tstate, _PyInterpreterFrame *frame); _PyInterpreterFrame * _PyFrame_Push(PyThreadState *tstate, PyFunctionObject *func); + +static inline +PyGenObject *_PyFrame_GetGenerator(_PyInterpreterFrame *frame) +{ + assert(frame->owner == FRAME_OWNED_BY_GENERATOR); + size_t offset_in_gen = offsetof(PyGenObject, gi_iframe); + return (PyGenObject *)(((char *)frame) - offset_in_gen); +} + #ifdef __cplusplus } #endif diff --git a/Modules/_xxsubinterpretersmodule.c b/Modules/_xxsubinterpretersmodule.c index 846b24d5efa9a6..0e37ce0aa3fe37 100644 --- a/Modules/_xxsubinterpretersmodule.c +++ b/Modules/_xxsubinterpretersmodule.c @@ -1843,10 +1843,7 @@ _is_running(PyInterpreterState *interp) if (frame == NULL) { return 0; } - - int executing = _PyFrame_IsExecuting(frame); - - return executing; + return 1; } static int diff --git a/Objects/frameobject.c b/Objects/frameobject.c index eb7fdb30cd75e6..d1e1ce82298c6b 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -434,6 +434,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } + PyFrameState state = PyFrame_GetState(f); /* * This code preserves the historical restrictions on * setting the line number of a frame. @@ -442,28 +443,31 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore * In addition, jumps are forbidden when not tracing, * as this is a debugging feature. */ - switch(f->f_frame->f_state) { - case FRAME_CREATED: + switch(PyThreadState_GET()->tracing_what) { + case PyTrace_EXCEPTION: + PyErr_SetString(PyExc_ValueError, + "can only jump from a 'line' trace event"); + return -1; + case PyTrace_CALL: PyErr_Format(PyExc_ValueError, "can't jump from the 'call' trace event of a new frame"); return -1; - case FRAME_RETURNED: - case FRAME_UNWINDING: - case FRAME_RAISED: - case FRAME_CLEARED: + case PyTrace_LINE: + break; + case PyTrace_RETURN: + if (state == FRAME_SUSPENDED) { + break; + } + /* fall through */ + default: PyErr_SetString(PyExc_ValueError, "can only jump from a 'line' trace event"); return -1; - case FRAME_EXECUTING: - case FRAME_SUSPENDED: - /* You can only do this from within a trace function, not via - * _getframe or similar hackery. */ - if (!f->f_trace) { - PyErr_Format(PyExc_ValueError, - "f_lineno can only be set by a trace function"); - return -1; - } - break; + } + if (!f->f_trace) { + PyErr_Format(PyExc_ValueError, + "f_lineno can only be set by a trace function"); + return -1; } int new_lineno; @@ -549,8 +553,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore PyErr_SetString(PyExc_ValueError, msg); return -1; } - /* Unwind block stack. */ - if (f->f_frame->f_state == FRAME_SUSPENDED) { + if (state == FRAME_SUSPENDED) { /* Account for value popped by yield */ start_stack = pop_value(start_stack); } @@ -617,7 +620,9 @@ frame_dealloc(PyFrameObject *f) { /* It is the responsibility of the owning generator/coroutine * to have cleared the generator pointer */ - assert(!f->f_frame->is_generator); + + assert(f->f_frame->owner != FRAME_OWNED_BY_GENERATOR || + _PyFrame_GetGenerator(f->f_frame)->gi_frame_state == FRAME_CLEARED); if (_PyObject_GC_IS_TRACKED(f)) { _PyObject_GC_UNTRACK(f); @@ -627,8 +632,7 @@ frame_dealloc(PyFrameObject *f) PyCodeObject *co = NULL; /* Kill all local variables including specials, if we own them */ - if (f->f_owns_frame) { - f->f_owns_frame = 0; + if (f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) { assert(f->f_frame == (_PyInterpreterFrame *)f->_f_frame_data); _PyInterpreterFrame *frame = (_PyInterpreterFrame *)f->_f_frame_data; /* Don't clear code object until the end */ @@ -653,7 +657,7 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) { Py_VISIT(f->f_back); Py_VISIT(f->f_trace); - if (f->f_owns_frame == 0) { + if (f->f_frame->owner != FRAME_OWNED_BY_FRAME_OBJECT) { return 0; } assert(f->f_frame->frame_obj == NULL); @@ -663,13 +667,6 @@ frame_traverse(PyFrameObject *f, visitproc visit, void *arg) static int frame_tp_clear(PyFrameObject *f) { - /* Before anything else, make sure that this frame is clearly marked - * as being defunct! Else, e.g., a generator reachable from this - * frame may also point to this frame, believe itself to still be - * active, and try cleaning up this frame again. - */ - f->f_frame->f_state = FRAME_CLEARED; - Py_CLEAR(f->f_trace); /* locals and stack */ @@ -685,19 +682,25 @@ frame_tp_clear(PyFrameObject *f) static PyObject * frame_clear(PyFrameObject *f, PyObject *Py_UNUSED(ignored)) { - if (_PyFrame_IsExecuting(f->f_frame)) { - PyErr_SetString(PyExc_RuntimeError, - "cannot clear an executing frame"); - return NULL; + if (f->f_frame->owner == FRAME_OWNED_BY_GENERATOR) { + PyGenObject *gen = _PyFrame_GetGenerator(f->f_frame); + if (gen->gi_frame_state == FRAME_EXECUTING) { + goto running; + } + _PyGen_Finalize((PyObject *)gen); } - if (f->f_frame->is_generator) { - assert(!f->f_owns_frame); - size_t offset_in_gen = offsetof(PyGenObject, gi_iframe); - PyObject *gen = (PyObject *)(((char *)f->f_frame) - offset_in_gen); - _PyGen_Finalize(gen); + else if (f->f_frame->owner == FRAME_OWNED_BY_THREAD) { + goto running; + } + else { + assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); + (void)frame_tp_clear(f); } - (void)frame_tp_clear(f); Py_RETURN_NONE; +running: + PyErr_SetString(PyExc_RuntimeError, + "cannot clear an executing frame"); + return NULL; } PyDoc_STRVAR(clear__doc__, @@ -829,7 +832,7 @@ PyFrame_New(PyThreadState *tstate, PyCodeObject *code, } init_frame((_PyInterpreterFrame *)f->_f_frame_data, func, locals); f->f_frame = (_PyInterpreterFrame *)f->_f_frame_data; - f->f_owns_frame = 1; + f->f_frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; Py_DECREF(func); _PyObject_GC_TRACK(f); return f; @@ -891,7 +894,7 @@ _PyFrame_FastToLocalsWithError(_PyInterpreterFrame *frame) { PyObject *name = PyTuple_GET_ITEM(co->co_localsplusnames, i); PyObject *value = fast[i]; - if (frame->f_state != FRAME_CLEARED) { + if (frame->stacktop) { if (kind & CO_FAST_FREE) { // The cell was set by COPY_FREE_VARS. assert(value != NULL && PyCell_Check(value)); @@ -1028,7 +1031,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { - if (f == NULL || f->f_frame->f_state == FRAME_CLEARED) { + if (f == NULL || PyFrame_GetState(f) == FRAME_CLEARED) { return; } _PyFrame_LocalsToFast(f->f_frame, clear); @@ -1086,3 +1089,36 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) return _PyEval_GetBuiltins(tstate); } + + +PyFrameState +PyFrame_GetState(PyFrameObject *frame) +{ + switch(frame->f_frame->owner) { + case FRAME_OWNED_BY_GENERATOR: + { + PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); + return gen->gi_frame_state; + } + case FRAME_OWNED_BY_THREAD: + { + int opcode = PyBytes_AS_STRING(frame->f_frame->f_code->co_code) + [frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; + switch(opcode) { + case COPY_FREE_VARS: + case MAKE_CELL: + case RETURN_GENERATOR: + /* Frame not fully initialized */ + return FRAME_CREATED; + default: + return FRAME_EXECUTING; + } + } + case FRAME_OWNED_BY_FRAME_OBJECT: + return FRAME_COMPLETED; + case FRAME_CLEARED: + return FRAME_CLEARED; + } + Py_UNREACHABLE(); +} + diff --git a/Objects/genobject.c b/Objects/genobject.c index 6551b939c4590c..fbfaf9b44714bd 100644 --- a/Objects/genobject.c +++ b/Objects/genobject.c @@ -35,9 +35,10 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg) Py_VISIT(gen->gi_code); Py_VISIT(gen->gi_name); Py_VISIT(gen->gi_qualname); - if (gen->gi_frame_valid) { + if (gen->gi_frame_state < FRAME_CLEARED) { _PyInterpreterFrame *frame = (_PyInterpreterFrame *)(gen->gi_iframe); - assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0); + assert(frame->frame_obj == NULL || + frame->frame_obj->f_frame->owner == FRAME_OWNED_BY_GENERATOR); int err = _PyFrame_Traverse(frame, visit, arg); if (err) { return err; @@ -55,7 +56,7 @@ _PyGen_Finalize(PyObject *self) PyObject *res = NULL; PyObject *error_type, *error_value, *error_traceback; - if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted((_PyInterpreterFrame *)gen->gi_iframe)) { + if (gen->gi_frame_state >= FRAME_COMPLETED) { /* Generator isn't paused, so no need to close */ return; } @@ -87,7 +88,7 @@ _PyGen_Finalize(PyObject *self) issue a RuntimeWarning. */ if (gen->gi_code != NULL && ((PyCodeObject *)gen->gi_code)->co_flags & CO_COROUTINE && - ((_PyInterpreterFrame *)gen->gi_iframe)->f_state == FRAME_CREATED) + gen->gi_frame_state == FRAME_CREATED) { _PyErr_WarnUnawaitedCoroutine((PyObject *)gen); } @@ -130,10 +131,9 @@ gen_dealloc(PyGenObject *gen) and GC_Del. */ Py_CLEAR(((PyAsyncGenObject*)gen)->ag_origin_or_finalizer); } - if (gen->gi_frame_valid) { + if (gen->gi_frame_state < FRAME_CLEARED) { _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; - gen->gi_frame_valid = 0; - frame->is_generator = false; + gen->gi_frame_state = FRAME_CLEARED; frame->previous = NULL; _PyFrame_Clear(frame); } @@ -156,7 +156,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyObject *result; *presult = NULL; - if (frame->f_state == FRAME_CREATED && arg && arg != Py_None) { + if (gen->gi_frame_state == FRAME_CREATED && arg && arg != Py_None) { const char *msg = "can't send non-None value to a " "just-started generator"; if (PyCoro_CheckExact(gen)) { @@ -169,7 +169,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_TypeError, msg); return PYGEN_ERROR; } - if (gen->gi_frame_valid && _PyFrame_IsExecuting(frame)) { + if (gen->gi_frame_state == FRAME_EXECUTING) { const char *msg = "generator already executing"; if (PyCoro_CheckExact(gen)) { msg = "coroutine already executing"; @@ -180,7 +180,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, PyErr_SetString(PyExc_ValueError, msg); return PYGEN_ERROR; } - if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) { + if (gen->gi_frame_state >= FRAME_COMPLETED) { if (PyCoro_CheckExact(gen) && !closing) { /* `gen` is an exhausted coroutine: raise an error, except when called from gen_close(), which should @@ -199,8 +199,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, return PYGEN_ERROR; } - assert(gen->gi_frame_valid); - assert(_PyFrame_IsRunnable(frame)); + assert(gen->gi_frame_state < FRAME_EXECUTING); /* Push arg onto the frame's value stack */ result = arg ? arg : Py_None; Py_INCREF(result); @@ -216,7 +215,11 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, _PyErr_ChainStackItem(NULL); } + gen->gi_frame_state = FRAME_EXECUTING; result = _PyEval_EvalFrame(tstate, frame, exc); + if (gen->gi_frame_state == FRAME_EXECUTING) { + gen->gi_frame_state = FRAME_COMPLETED; + } tstate->exc_info = gen->gi_exc_state.previous_item; gen->gi_exc_state.previous_item = NULL; @@ -229,7 +232,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* If the generator just returned (as opposed to yielding), signal * that the generator is exhausted. */ if (result) { - if (!_PyFrameHasCompleted(frame)) { + if (gen->gi_frame_state == FRAME_SUSPENDED) { *presult = result; return PYGEN_NEXT; } @@ -265,8 +268,7 @@ gen_send_ex2(PyGenObject *gen, PyObject *arg, PyObject **presult, /* first clean reference cycle through stored exception traceback */ _PyErr_ClearExcState(&gen->gi_exc_state); - frame->is_generator = false; - gen->gi_frame_valid = 0; + gen->gi_frame_state = FRAME_CLEARED; _PyFrame_Clear(frame); *presult = result; return result ? PYGEN_RETURN : PYGEN_ERROR; @@ -347,7 +349,7 @@ _PyGen_yf(PyGenObject *gen) { PyObject *yf = NULL; - if (gen->gi_frame_valid) { + if (gen->gi_frame_state < FRAME_CLEARED) { _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; PyObject *bytecode = gen->gi_code->co_code; unsigned char *code = (unsigned char *)PyBytes_AS_STRING(bytecode); @@ -380,11 +382,10 @@ gen_close(PyGenObject *gen, PyObject *args) int err = 0; if (yf) { - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; - PyFrameState state = frame->f_state; - frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; err = gen_close_iter(yf); - frame->f_state = state; + gen->gi_frame_state = state; Py_DECREF(yf); } if (err == 0) @@ -431,10 +432,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 = frame->f_state; - frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; err = gen_close_iter(yf); - frame->f_state = state; + gen->gi_frame_state = state; Py_DECREF(yf); if (err < 0) return gen_send_ex(gen, Py_None, 1, 0); @@ -453,11 +454,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 = frame->f_state; - frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; ret = _gen_throw((PyGenObject *)yf, close_on_genexit, typ, val, tb); - frame->f_state = state; + gen->gi_frame_state = state; tstate->cframe->current_frame = prev; frame->previous = NULL; } else { @@ -471,17 +472,17 @@ _gen_throw(PyGenObject *gen, int close_on_genexit, Py_DECREF(yf); goto throw_here; } - PyFrameState state = frame->f_state; - frame->f_state = FRAME_EXECUTING; + PyFrameState state = gen->gi_frame_state; + gen->gi_frame_state = FRAME_EXECUTING; ret = PyObject_CallFunctionObjArgs(meth, typ, val, tb, NULL); - frame->f_state = state; + gen->gi_frame_state = state; Py_DECREF(meth); } Py_DECREF(yf); if (!ret) { PyObject *val; /* Pop subiterator from stack */ - assert(gen->gi_frame_valid); + assert(gen->gi_frame_state < FRAME_CLEARED); ret = _PyFrame_StackPop((_PyInterpreterFrame *)gen->gi_iframe); assert(ret == yf); Py_DECREF(ret); @@ -757,19 +758,16 @@ gen_getyieldfrom(PyGenObject *gen, void *Py_UNUSED(ignored)) static PyObject * gen_getrunning(PyGenObject *gen, void *Py_UNUSED(ignored)) { - if (gen->gi_frame_valid == 0) { - Py_RETURN_FALSE; + if (gen->gi_frame_state == FRAME_EXECUTING) { + Py_RETURN_TRUE; } - return PyBool_FromLong(_PyFrame_IsExecuting((_PyInterpreterFrame *)gen->gi_iframe)); + Py_RETURN_FALSE; } static PyObject * gen_getsuspended(PyGenObject *gen, void *Py_UNUSED(ignored)) { - if (gen->gi_frame_valid == 0) { - Py_RETURN_FALSE; - } - return PyBool_FromLong(((_PyInterpreterFrame *)gen->gi_iframe)->f_state == FRAME_SUSPENDED); + return PyBool_FromLong(gen->gi_frame_state == FRAME_SUSPENDED); } static PyObject * @@ -778,7 +776,7 @@ _gen_getframe(PyGenObject *gen, const char *const name) if (PySys_Audit("object.__getattr__", "Os", gen, name) < 0) { return NULL; } - if (gen->gi_frame_valid == 0) { + if (gen->gi_frame_state == FRAME_CLEARED) { Py_RETURN_NONE; } return _Py_XNewRef((PyObject *)_PyFrame_GetFrameObject((_PyInterpreterFrame *)gen->gi_iframe)); @@ -900,7 +898,7 @@ make_gen(PyTypeObject *type, PyFunctionObject *func) if (gen == NULL) { return NULL; } - gen->gi_frame_valid = 0; + gen->gi_frame_state = FRAME_CLEARED; gen->gi_code = (PyCodeObject *)func->func_code; Py_INCREF(gen->gi_code); gen->gi_weakreflist = NULL; @@ -973,14 +971,13 @@ gen_new_with_qualname(PyTypeObject *type, PyFrameObject *f, } /* Copy the frame */ assert(f->f_frame->frame_obj == NULL); - assert(f->f_owns_frame); + assert(f->f_frame->owner == FRAME_OWNED_BY_FRAME_OBJECT); _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; _PyFrame_Copy((_PyInterpreterFrame *)f->_f_frame_data, frame); - gen->gi_frame_valid = 1; + gen->gi_frame_state = FRAME_CREATED; assert(frame->frame_obj == f); - f->f_owns_frame = 0; f->f_frame = frame; - frame->is_generator = true; + frame->owner = FRAME_OWNED_BY_GENERATOR; assert(PyObject_GC_IsTracked((PyObject *)f)); gen->gi_code = PyFrame_GetCode(f); Py_INCREF(gen->gi_code); @@ -1115,19 +1112,19 @@ coro_get_cr_await(PyCoroObject *coro, void *Py_UNUSED(ignored)) static PyObject * cr_getsuspended(PyCoroObject *coro, void *Py_UNUSED(ignored)) { - if (coro->cr_frame_valid == 0) { - Py_RETURN_FALSE; + if (coro->cr_frame_state == FRAME_SUSPENDED) { + Py_RETURN_TRUE; } - return PyBool_FromLong(((_PyInterpreterFrame *)coro->cr_iframe)->f_state == FRAME_SUSPENDED); + Py_RETURN_FALSE; } static PyObject * cr_getrunning(PyCoroObject *coro, void *Py_UNUSED(ignored)) { - if (coro->cr_frame_valid == 0) { - Py_RETURN_FALSE; + if (coro->cr_frame_state == FRAME_EXECUTING) { + Py_RETURN_TRUE; } - return PyBool_FromLong(_PyFrame_IsExecuting((_PyInterpreterFrame *)coro->cr_iframe)); + Py_RETURN_FALSE; } static PyObject * @@ -2064,7 +2061,6 @@ static PyObject * async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) { PyGenObject *gen = (PyGenObject*)o->agt_gen; - _PyInterpreterFrame *frame = (_PyInterpreterFrame *)gen->gi_iframe; PyObject *retval; if (o->agt_state == AWAITABLE_STATE_CLOSED) { @@ -2074,7 +2070,7 @@ async_gen_athrow_send(PyAsyncGenAThrow *o, PyObject *arg) return NULL; } - if (gen->gi_frame_valid == 0 || _PyFrameHasCompleted(frame)) { + if (gen->gi_frame_state >= FRAME_COMPLETED) { o->agt_state = AWAITABLE_STATE_CLOSED; PyErr_SetNone(PyExc_StopIteration); return NULL; diff --git a/Python/ceval.c b/Python/ceval.c index 81759ad770b1e3..17f2a0e664d128 100644 --- a/Python/ceval.c +++ b/Python/ceval.c @@ -1739,7 +1739,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int PREDICTED(RESUME_QUICK); assert(tstate->cframe == &cframe); assert(frame == cframe.current_frame); - frame->f_state = FRAME_EXECUTING; if (_Py_atomic_load_relaxed(eval_breaker) && oparg < 2) { goto handle_eval_breaker; } @@ -2382,7 +2381,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(RETURN_VALUE) { PyObject *retval = POP(); assert(EMPTY()); - frame->f_state = FRAME_RETURNED; _PyFrame_SetStackPointer(frame, stack_pointer); TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); @@ -2594,7 +2592,7 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int TARGET(YIELD_VALUE) { assert(frame->is_entry); PyObject *retval = POP(); - frame->f_state = FRAME_SUSPENDED; + _PyFrame_GetGenerator(frame)->gi_frame_state = FRAME_SUSPENDED; _PyFrame_SetStackPointer(frame, stack_pointer); TRACE_FUNCTION_EXIT(); DTRACE_FUNCTION_EXIT(); @@ -4078,7 +4076,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int * generator or coroutine, so we deliberately do not check it here. * (see bpo-30039). */ - frame->f_state = FRAME_EXECUTING; JUMPTO(oparg); DISPATCH(); } @@ -5263,9 +5260,8 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int _PyInterpreterFrame *gen_frame = (_PyInterpreterFrame *)gen->gi_iframe; _PyFrame_Copy(frame, gen_frame); assert(frame->frame_obj == NULL); - gen->gi_frame_valid = 1; - gen_frame->is_generator = true; - gen_frame->f_state = FRAME_CREATED; + gen->gi_frame_state = FRAME_CREATED; + gen_frame->owner = FRAME_OWNED_BY_GENERATOR; _Py_LeaveRecursiveCall(tstate); if (!frame->is_entry) { _PyInterpreterFrame *prev = frame->previous; @@ -5438,41 +5434,47 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int int instr_prev = frame->f_lasti; frame->f_lasti = INSTR_OFFSET(); TRACING_NEXTOPARG(); - if (opcode == RESUME) { - if (oparg < 2) { - CHECK_EVAL_BREAKER(); - } - /* Call tracing */ - TRACE_FUNCTION_ENTRY(); - DTRACE_FUNCTION_ENTRY(); - } - else if (frame->f_state > FRAME_CREATED) { - /* line-by-line tracing support */ - if (PyDTrace_LINE_ENABLED()) { - maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); - } - - if (cframe.use_tracing && - tstate->c_tracefunc != NULL && !tstate->tracing) { - int err; - /* see maybe_call_line_trace() - for expository comments */ - _PyFrame_SetStackPointer(frame, stack_pointer); - - err = maybe_call_line_trace(tstate->c_tracefunc, - tstate->c_traceobj, - tstate, frame, instr_prev); - if (err) { - /* trace function raised an exception */ - next_instr++; - goto error; + switch(opcode) { + case COPY_FREE_VARS: + case MAKE_CELL: + case RETURN_GENERATOR: + /* Frame not fully initialized */ + break; + case RESUME: + if (oparg < 2) { + CHECK_EVAL_BREAKER(); + } + /* Call tracing */ + TRACE_FUNCTION_ENTRY(); + DTRACE_FUNCTION_ENTRY(); + break; + default: + /* line-by-line tracing support */ + if (PyDTrace_LINE_ENABLED()) { + maybe_dtrace_line(frame, &tstate->trace_info, instr_prev); } - /* Reload possibly changed frame fields */ - JUMPTO(frame->f_lasti); - stack_pointer = _PyFrame_GetStackPointer(frame); - frame->stacktop = -1; - } + if (cframe.use_tracing && + tstate->c_tracefunc != NULL && !tstate->tracing) { + int err; + /* see maybe_call_line_trace() + for expository comments */ + _PyFrame_SetStackPointer(frame, stack_pointer); + + err = maybe_call_line_trace(tstate->c_tracefunc, + tstate->c_traceobj, + tstate, frame, instr_prev); + if (err) { + /* trace function raised an exception */ + next_instr++; + goto error; + } + /* Reload possibly changed frame fields */ + JUMPTO(frame->f_lasti); + + stack_pointer = _PyFrame_GetStackPointer(frame); + frame->stacktop = -1; + } } } TRACING_NEXTOPARG(); @@ -5567,65 +5569,63 @@ MISS_WITH_INLINE_CACHE(STORE_SUBSCR) if (tstate->c_tracefunc != NULL) { /* Make sure state is set to FRAME_UNWINDING for tracing */ - frame->f_state = FRAME_UNWINDING; call_exc_trace(tstate->c_tracefunc, tstate->c_traceobj, tstate, frame); } exception_unwind: - frame->f_state = FRAME_UNWINDING; - /* We can't use frame->f_lasti here, as RERAISE may have set it */ - int offset = INSTR_OFFSET()-1; - int level, handler, lasti; - if (get_exception_handler(frame->f_code, offset, &level, &handler, &lasti) == 0) { - // No handlers, so exit. - assert(_PyErr_Occurred(tstate)); - - /* Pop remaining stack entries. */ - PyObject **stackbase = _PyFrame_Stackbase(frame); - while (stack_pointer > stackbase) { - PyObject *o = POP(); - Py_XDECREF(o); - } - assert(STACK_LEVEL() == 0); - _PyFrame_SetStackPointer(frame, stack_pointer); - frame->f_state = FRAME_RAISED; - TRACE_FUNCTION_UNWIND(); - DTRACE_FUNCTION_EXIT(); - goto exit_unwind; - } + { + /* We can't use frame->f_lasti here, as RERAISE may have set it */ + int offset = INSTR_OFFSET()-1; + int level, handler, lasti; + if (get_exception_handler(frame->f_code, offset, &level, &handler, &lasti) == 0) { + // No handlers, so exit. + assert(_PyErr_Occurred(tstate)); + + /* Pop remaining stack entries. */ + PyObject **stackbase = _PyFrame_Stackbase(frame); + while (stack_pointer > stackbase) { + PyObject *o = POP(); + Py_XDECREF(o); + } + assert(STACK_LEVEL() == 0); + _PyFrame_SetStackPointer(frame, stack_pointer); + TRACE_FUNCTION_UNWIND(); + DTRACE_FUNCTION_EXIT(); + goto exit_unwind; + } - assert(STACK_LEVEL() >= level); - PyObject **new_top = _PyFrame_Stackbase(frame) + level; - while (stack_pointer > new_top) { - PyObject *v = POP(); - Py_XDECREF(v); - } - PyObject *exc, *val, *tb; - if (lasti) { - PyObject *lasti = PyLong_FromLong(frame->f_lasti); - if (lasti == NULL) { - goto exception_unwind; + assert(STACK_LEVEL() >= level); + PyObject **new_top = _PyFrame_Stackbase(frame) + level; + while (stack_pointer > new_top) { + PyObject *v = POP(); + Py_XDECREF(v); } - PUSH(lasti); + PyObject *exc, *val, *tb; + if (lasti) { + PyObject *lasti = PyLong_FromLong(frame->f_lasti); + if (lasti == NULL) { + goto exception_unwind; + } + PUSH(lasti); + } + _PyErr_Fetch(tstate, &exc, &val, &tb); + /* Make the raw exception data + available to the handler, + so a program can emulate the + Python main loop. */ + _PyErr_NormalizeException(tstate, &exc, &val, &tb); + if (tb != NULL) + PyException_SetTraceback(val, tb); + else + PyException_SetTraceback(val, Py_None); + Py_XDECREF(tb); + Py_XDECREF(exc); + PUSH(val); + JUMPTO(handler); + /* Resume normal execution */ + DISPATCH(); } - _PyErr_Fetch(tstate, &exc, &val, &tb); - /* Make the raw exception data - available to the handler, - so a program can emulate the - Python main loop. */ - _PyErr_NormalizeException(tstate, &exc, &val, &tb); - if (tb != NULL) - PyException_SetTraceback(val, tb); - else - PyException_SetTraceback(val, Py_None); - Py_XDECREF(tb); - Py_XDECREF(exc); - PUSH(val); - JUMPTO(handler); - /* Resume normal execution */ - frame->f_state = FRAME_EXECUTING; - DISPATCH(); } exit_unwind: @@ -6189,6 +6189,7 @@ _PyEvalFramePushAndInit(PyThreadState *tstate, PyFunctionObject *func, localsarray[i] = NULL; } if (initialize_locals(tstate, func, localsarray, args, argcount, kwnames)) { + assert(frame->owner != FRAME_OWNED_BY_GENERATOR); _PyFrame_Clear(frame); return NULL; } @@ -6212,7 +6213,8 @@ static void _PyEvalFrameClearAndPop(PyThreadState *tstate, _PyInterpreterFrame * frame) { tstate->recursion_remaining--; - assert(frame->frame_obj == NULL || frame->frame_obj->f_owns_frame == 0); + assert(frame->frame_obj == NULL || frame->frame_obj->f_frame == frame); + assert(frame->owner == FRAME_OWNED_BY_THREAD); _PyFrame_Clear(frame); tstate->recursion_remaining++; _PyThreadState_PopFrame(tstate, frame); @@ -6690,6 +6692,8 @@ call_trace(Py_tracefunc func, PyObject *obj, if (f == NULL) { return -1; } + int old_what = tstate->tracing_what; + tstate->tracing_what = what; PyThreadState_EnterTracing(tstate); assert (frame->f_lasti >= 0); initialize_trace_info(&tstate->trace_info, frame); @@ -6697,6 +6701,7 @@ call_trace(Py_tracefunc func, PyObject *obj, result = func(obj, f, what, arg); f->f_lineno = 0; PyThreadState_LeaveTracing(tstate); + tstate->tracing_what = old_what; return result; } diff --git a/Python/frame.c b/Python/frame.c index 20b4f81425bc8b..3396ed8d2aeb0e 100644 --- a/Python/frame.c +++ b/Python/frame.c @@ -37,7 +37,8 @@ _PyFrame_MakeAndSetFrameObject(_PyInterpreterFrame *frame) Py_XDECREF(error_traceback); } else { - f->f_owns_frame = 0; + assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); + assert(frame->owner != FRAME_CLEARED); f->f_frame = frame; frame->frame_obj = f; PyErr_Restore(error_type, error_value, error_traceback); @@ -57,12 +58,13 @@ _PyFrame_Copy(_PyInterpreterFrame *src, _PyInterpreterFrame *dest) static void take_ownership(PyFrameObject *f, _PyInterpreterFrame *frame) { - assert(f->f_owns_frame == 0); + assert(frame->owner != FRAME_OWNED_BY_FRAME_OBJECT); + assert(frame->owner != FRAME_CLEARED); Py_ssize_t size = ((char*)&frame->localsplus[frame->stacktop]) - (char *)frame; memcpy((_PyInterpreterFrame *)f->_f_frame_data, frame, size); frame = (_PyInterpreterFrame *)f->_f_frame_data; - f->f_owns_frame = 1; f->f_frame = frame; + frame->owner = FRAME_OWNED_BY_FRAME_OBJECT; assert(f->f_back == NULL); if (frame->previous != NULL) { /* Link PyFrameObjects.f_back and remove link through _PyInterpreterFrame.previous */ @@ -88,7 +90,8 @@ _PyFrame_Clear(_PyInterpreterFrame *frame) { /* It is the responsibility of the owning generator/coroutine * to have cleared the enclosing generator, if any. */ - assert(!frame->is_generator); + assert(frame->owner != FRAME_OWNED_BY_GENERATOR || + _PyFrame_GetGenerator(frame)->gi_frame_state == FRAME_CLEARED); if (frame->frame_obj) { PyFrameObject *f = frame->frame_obj; frame->frame_obj = NULL; From cfdbe4e50cdbd6fbba570eddf5ec5801491a2fb2 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 17 Mar 2022 16:26:06 +0000 Subject: [PATCH 02/10] Add news --- .../Core and Builtins/2022-03-17-16-25-57.bpo-47045.xQgHul.rst | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-17-16-25-57.bpo-47045.xQgHul.rst diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-17-16-25-57.bpo-47045.xQgHul.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-17-16-25-57.bpo-47045.xQgHul.rst new file mode 100644 index 00000000000000..388258884299ec --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-17-16-25-57.bpo-47045.xQgHul.rst @@ -0,0 +1,3 @@ +Remove the ``f_state`` field from the _PyInterpreterFrame struct. Add the +``owner`` field to the _PyInterpreterFrame struct to make ownership explicit +to simplify clearing and deallocing frames and generators. From 65c1773007108c33607f8055eb56c9fbc605dbab Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 17 Mar 2022 16:43:50 +0000 Subject: [PATCH 03/10] Remove incorrect comment. --- Include/internal/pycore_frame.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 3267eab4fc07ab..4aa17c14860a4c 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -33,7 +33,6 @@ enum _frameowner { FRAME_OWNED_BY_THREAD = 0, FRAME_OWNED_BY_GENERATOR = 1, FRAME_OWNED_BY_FRAME_OBJECT = 2 - /* FRAME_CLEARED is also valid */ }; /* From bba6f5643fcd428cabcc3408072f5ce21dcb0f9d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 17 Mar 2022 16:45:09 +0000 Subject: [PATCH 04/10] Put function back to original header. --- Include/cpython/frameobject.h | 1 + Include/frameobject.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/frameobject.h b/Include/cpython/frameobject.h index e39a4f34435916..ebaecbed1b487b 100644 --- a/Include/cpython/frameobject.h +++ b/Include/cpython/frameobject.h @@ -24,3 +24,4 @@ PyAPI_FUNC(void) PyFrame_FastToLocals(PyFrameObject *); PyAPI_FUNC(void) _PyFrame_DebugMallocStats(FILE *out); +PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); diff --git a/Include/frameobject.h b/Include/frameobject.h index a59d88c14ad121..23eee64d19e174 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -22,7 +22,6 @@ typedef enum _framestate { FRAME_CLEARED = 4 } PyFrameState; -PyAPI_FUNC(PyFrameObject *) PyFrame_GetBack(PyFrameObject *frame); PyAPI_FUNC(PyFrameState) PyFrame_GetState(PyFrameObject *frame); From 4f60962b849ebbf9b971f72d0727f0b184987ae6 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Thu, 17 Mar 2022 16:52:02 +0000 Subject: [PATCH 05/10] Fix PyFrame_GetState --- Objects/frameobject.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index d1e1ce82298c6b..2a789417908e4b 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -1094,6 +1094,9 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) PyFrameState PyFrame_GetState(PyFrameObject *frame) { + if (frame->f_frame->stacktop == 0) { + return FRAME_CLEARED; + } switch(frame->f_frame->owner) { case FRAME_OWNED_BY_GENERATOR: { @@ -1102,6 +1105,9 @@ PyFrame_GetState(PyFrameObject *frame) } case FRAME_OWNED_BY_THREAD: { + if (frame->f_frame->f_lasti < 0) { + return FRAME_CREATED; + } int opcode = PyBytes_AS_STRING(frame->f_frame->f_code->co_code) [frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; switch(opcode) { @@ -1116,8 +1122,6 @@ PyFrame_GetState(PyFrameObject *frame) } case FRAME_OWNED_BY_FRAME_OBJECT: return FRAME_COMPLETED; - case FRAME_CLEARED: - return FRAME_CLEARED; } Py_UNREACHABLE(); } From 8cae06e4a87d393b8115a90827ae332332591360 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 18 Mar 2022 10:31:29 +0000 Subject: [PATCH 06/10] Make _PyFrame_GetState static for now. --- Include/frameobject.h | 11 ------ Objects/frameobject.c | 80 ++++++++++++++++++++++++------------------- 2 files changed, 45 insertions(+), 46 deletions(-) diff --git a/Include/frameobject.h b/Include/frameobject.h index 23eee64d19e174..adb628f6314fcf 100644 --- a/Include/frameobject.h +++ b/Include/frameobject.h @@ -14,17 +14,6 @@ extern "C" { # undef Py_CPYTHON_FRAMEOBJECT_H #endif -typedef enum _framestate { - FRAME_CREATED = -2, - FRAME_SUSPENDED = -1, - FRAME_EXECUTING = 0, - FRAME_COMPLETED = 1, - FRAME_CLEARED = 4 -} PyFrameState; - -PyAPI_FUNC(PyFrameState) PyFrame_GetState(PyFrameObject *frame); - - #ifdef __cplusplus } #endif diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 2a789417908e4b..2460f8c5883bf1 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -406,6 +406,51 @@ frame_stack_pop(PyFrameObject *f) Py_DECREF(v); } +typedef enum _framestate { + FRAME_CREATED = -2, + FRAME_SUSPENDED = -1, + FRAME_EXECUTING = 0, + FRAME_COMPLETED = 1, + FRAME_CLEARED = 4 +} PyFrameState; + + +static PyFrameState +_PyFrame_GetState(PyFrameObject *frame) +{ + if (frame->f_frame->stacktop == 0) { + return FRAME_CLEARED; + } + switch(frame->f_frame->owner) { + case FRAME_OWNED_BY_GENERATOR: + { + PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); + return gen->gi_frame_state; + } + case FRAME_OWNED_BY_THREAD: + { + if (frame->f_frame->f_lasti < 0) { + return FRAME_CREATED; + } + int opcode = PyBytes_AS_STRING(frame->f_frame->f_code->co_code) + [frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; + switch(opcode) { + case COPY_FREE_VARS: + case MAKE_CELL: + case RETURN_GENERATOR: + /* Frame not fully initialized */ + return FRAME_CREATED; + default: + return FRAME_EXECUTING; + } + } + case FRAME_OWNED_BY_FRAME_OBJECT: + return FRAME_COMPLETED; + } + Py_UNREACHABLE(); +} + + /* Setter for f_lineno - you can set f_lineno from within a trace function in * order to jump to a given line of code, subject to some restrictions. Most * lines are OK to jump to because they don't make any assumptions about the @@ -1091,38 +1136,3 @@ _PyEval_BuiltinsFromGlobals(PyThreadState *tstate, PyObject *globals) } -PyFrameState -PyFrame_GetState(PyFrameObject *frame) -{ - if (frame->f_frame->stacktop == 0) { - return FRAME_CLEARED; - } - switch(frame->f_frame->owner) { - case FRAME_OWNED_BY_GENERATOR: - { - PyGenObject *gen = _PyFrame_GetGenerator(frame->f_frame); - return gen->gi_frame_state; - } - case FRAME_OWNED_BY_THREAD: - { - if (frame->f_frame->f_lasti < 0) { - return FRAME_CREATED; - } - int opcode = PyBytes_AS_STRING(frame->f_frame->f_code->co_code) - [frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; - switch(opcode) { - case COPY_FREE_VARS: - case MAKE_CELL: - case RETURN_GENERATOR: - /* Frame not fully initialized */ - return FRAME_CREATED; - default: - return FRAME_EXECUTING; - } - } - case FRAME_OWNED_BY_FRAME_OBJECT: - return FRAME_COMPLETED; - } - Py_UNREACHABLE(); -} - From 1be0129568445dd628a56274e24bbbc5fb99de70 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 18 Mar 2022 11:10:54 +0000 Subject: [PATCH 07/10] Make PyFrame_GetState static for now. --- Include/internal/pycore_frame.h | 7 +++++++ Objects/frameobject.c | 13 ++----------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Include/internal/pycore_frame.h b/Include/internal/pycore_frame.h index 4aa17c14860a4c..4492b2169ef7c8 100644 --- a/Include/internal/pycore_frame.h +++ b/Include/internal/pycore_frame.h @@ -28,6 +28,13 @@ extern void _PyFrame_Fini(PyInterpreterState *interp); /* other API */ +typedef enum _framestate { + FRAME_CREATED = -2, + FRAME_SUSPENDED = -1, + FRAME_EXECUTING = 0, + FRAME_COMPLETED = 1, + FRAME_CLEARED = 4 +} PyFrameState; enum _frameowner { FRAME_OWNED_BY_THREAD = 0, diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 2460f8c5883bf1..a0d281fdad0a27 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -406,15 +406,6 @@ frame_stack_pop(PyFrameObject *f) Py_DECREF(v); } -typedef enum _framestate { - FRAME_CREATED = -2, - FRAME_SUSPENDED = -1, - FRAME_EXECUTING = 0, - FRAME_COMPLETED = 1, - FRAME_CLEARED = 4 -} PyFrameState; - - static PyFrameState _PyFrame_GetState(PyFrameObject *frame) { @@ -479,7 +470,7 @@ frame_setlineno(PyFrameObject *f, PyObject* p_new_lineno, void *Py_UNUSED(ignore return -1; } - PyFrameState state = PyFrame_GetState(f); + PyFrameState state = _PyFrame_GetState(f); /* * This code preserves the historical restrictions on * setting the line number of a frame. @@ -1076,7 +1067,7 @@ _PyFrame_LocalsToFast(_PyInterpreterFrame *frame, int clear) void PyFrame_LocalsToFast(PyFrameObject *f, int clear) { - if (f == NULL || PyFrame_GetState(f) == FRAME_CLEARED) { + if (f == NULL || _PyFrame_GetState(f) == FRAME_CLEARED) { return; } _PyFrame_LocalsToFast(f->f_frame, clear); From 3cf7d0ac04c51f0f9cb484a0946fc6cbe08d1f2d Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Fri, 18 Mar 2022 15:06:43 +0000 Subject: [PATCH 08/10] Make sign of field explicit. --- Include/cpython/genobject.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Include/cpython/genobject.h b/Include/cpython/genobject.h index 50aea8481839a6..40eaa19d3fad94 100644 --- a/Include/cpython/genobject.h +++ b/Include/cpython/genobject.h @@ -27,7 +27,7 @@ extern "C" { char prefix##_closed; \ char prefix##_running_async; \ /* The frame */ \ - char prefix##_frame_state; \ + int8_t prefix##_frame_state; \ PyObject *prefix##_iframe[1]; typedef struct { From 82b1dbdf5b5dcf5deee0c6507298c4c4063d4a17 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 21 Mar 2022 15:54:13 +0000 Subject: [PATCH 09/10] Update _PyFrame_GetState to use co_code_adaptive --- Objects/frameobject.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index 37cfd313b7e118..e9d155a27c7db3 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -429,9 +429,9 @@ _PyFrame_GetState(PyFrameObject *frame) if (frame->f_frame->f_lasti < 0) { return FRAME_CREATED; } - int opcode = PyBytes_AS_STRING(frame->f_frame->f_code->co_code) - [frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; - switch(opcode) { + char *code = frame->f_frame->f_code->co_code_adaptive; + int opcode = code[frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; + switch(_PyOpcode_Deopt[opcode]) { case COPY_FREE_VARS: case MAKE_CELL: case RETURN_GENERATOR: From 5225175bc1688cf573381ce304f818a88139bf68 Mon Sep 17 00:00:00 2001 From: Mark Shannon Date: Mon, 21 Mar 2022 17:51:09 +0000 Subject: [PATCH 10/10] Make signed-ness of opcode explicit --- Objects/frameobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/frameobject.c b/Objects/frameobject.c index e9d155a27c7db3..5c6a8bcb9008da 100644 --- a/Objects/frameobject.c +++ b/Objects/frameobject.c @@ -429,7 +429,7 @@ _PyFrame_GetState(PyFrameObject *frame) if (frame->f_frame->f_lasti < 0) { return FRAME_CREATED; } - char *code = frame->f_frame->f_code->co_code_adaptive; + uint8_t *code = (uint8_t *)frame->f_frame->f_code->co_code_adaptive; int opcode = code[frame->f_frame->f_lasti*sizeof(_Py_CODEUNIT)]; switch(_PyOpcode_Deopt[opcode]) { case COPY_FREE_VARS: