diff options
author | Mark Shannon <mark@hotpy.org> | 2021-07-26 10:22:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2021-07-26 10:22:16 (GMT) |
commit | ae0a2b756255629140efcbe57fc2e714f0267aa3 (patch) | |
tree | 8710e8c7a398c9ec0add227fab607f367242a7e5 /Python/frame.c | |
parent | 0363a4014d90df17a29042de008ef0b659f92505 (diff) | |
download | cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.zip cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.gz cpython-ae0a2b756255629140efcbe57fc2e714f0267aa3.tar.bz2 |
bpo-44590: Lazily allocate frame objects (GH-27077)
* Convert "specials" array to InterpreterFrame struct, adding f_lasti, f_state and other non-debug FrameObject fields to it.
* Refactor, calls pushing the call to the interpreter upward toward _PyEval_Vector.
* Compute f_back when on thread stack, only filling in value when frame object outlives stack invocation.
* Move ownership of InterpreterFrame in generator from frame object to generator object.
* Do not create frame objects for Python calls.
* Do not create frame objects for generators.
Diffstat (limited to 'Python/frame.c')
-rw-r--r-- | Python/frame.c | 135 |
1 files changed, 135 insertions, 0 deletions
diff --git a/Python/frame.c b/Python/frame.c new file mode 100644 index 0000000..ae42843 --- /dev/null +++ b/Python/frame.c @@ -0,0 +1,135 @@ + +#include "Python.h" +#include "frameobject.h" +#include "pycore_frame.h" +#include "pycore_object.h" // _PyObject_GC_UNTRACK() + +int +_PyFrame_Traverse(InterpreterFrame *frame, visitproc visit, void *arg) +{ + Py_VISIT(frame->frame_obj); + Py_VISIT(frame->f_globals); + Py_VISIT(frame->f_builtins); + Py_VISIT(frame->f_locals); + Py_VISIT(frame->f_code); + /* locals */ + PyObject **locals = _PyFrame_GetLocalsArray(frame); + for (int i = 0; i < frame->nlocalsplus; i++) { + Py_VISIT(locals[i]); + } + /* stack */ + for (int i = 0; i <frame->stackdepth; i++) { + Py_VISIT(frame->stack[i]); + } + return 0; +} + +PyFrameObject * +_PyFrame_MakeAndSetFrameObject(InterpreterFrame *frame) +{ + assert(frame->frame_obj == NULL); + PyObject *error_type, *error_value, *error_traceback; + PyErr_Fetch(&error_type, &error_value, &error_traceback); + PyFrameObject *f = _PyFrame_New_NoTrack(frame, 0); + if (f == NULL) { + Py_XDECREF(error_type); + Py_XDECREF(error_value); + Py_XDECREF(error_traceback); + } + else { + PyErr_Restore(error_type, error_value, error_traceback); + } + frame->frame_obj = f; + return f; +} + + +static InterpreterFrame * +copy_frame_to_heap(InterpreterFrame *frame) +{ + + Py_ssize_t size = ((char*)&frame->stack[frame->stackdepth]) - (char *)_PyFrame_GetLocalsArray(frame); + PyObject **copy = PyMem_Malloc(size); + if (copy == NULL) { + PyErr_NoMemory(); + return NULL; + } + PyObject **locals = _PyFrame_GetLocalsArray(frame); + memcpy(copy, locals, size); + InterpreterFrame *res = (InterpreterFrame *)(copy + frame->nlocalsplus); + return res; +} + +static inline void +clear_specials(InterpreterFrame *frame) +{ + frame->generator = NULL; + Py_XDECREF(frame->frame_obj); + Py_XDECREF(frame->f_locals); + Py_DECREF(frame->f_globals); + Py_DECREF(frame->f_builtins); + Py_DECREF(frame->f_code); +} + +static void +take_ownership(PyFrameObject *f, InterpreterFrame *frame) +{ + assert(f->f_own_locals_memory == 0); + assert(frame->frame_obj == NULL); + + f->f_own_locals_memory = 1; + f->f_frame = frame; + assert(f->f_back == NULL); + if (frame->previous != NULL) { + /* Link PyFrameObjects.f_back and remove link through InterpreterFrame.previous */ + PyFrameObject *back = _PyFrame_GetFrameObject(frame->previous); + if (back == NULL) { + /* Memory error here. */ + assert(PyErr_ExceptionMatches(PyExc_MemoryError)); + /* Nothing we can do about it */ + PyErr_Clear(); + _PyErr_WriteUnraisableMsg("Out of memory lazily allocating frame->f_back", NULL); + } + else { + f->f_back = (PyFrameObject *)Py_NewRef(back); + } + frame->previous = NULL; + } + if (!_PyObject_GC_IS_TRACKED((PyObject *)f)) { + _PyObject_GC_TRACK((PyObject *)f); + } +} + +int +_PyFrame_Clear(InterpreterFrame * frame, int take) +{ + PyObject **localsarray = ((PyObject **)frame)-frame->nlocalsplus; + if (frame->frame_obj) { + PyFrameObject *f = frame->frame_obj; + frame->frame_obj = NULL; + if (Py_REFCNT(f) > 1) { + if (!take) { + frame = copy_frame_to_heap(frame); + if (frame == NULL) { + return -1; + } + } + take_ownership(f, frame); + Py_DECREF(f); + return 0; + } + Py_DECREF(f); + } + for (int i = 0; i < frame->nlocalsplus; i++) { + Py_XDECREF(localsarray[i]); + } + assert(frame->stackdepth >= 0); + for (int i = 0; i < frame->stackdepth; i++) { + Py_DECREF(frame->stack[i]); + } + clear_specials(frame); + if (take) { + PyMem_Free(_PyFrame_GetLocalsArray(frame)); + } + return 0; +} |