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/pystate.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/pystate.c')
-rw-r--r-- | Python/pystate.c | 81 |
1 files changed, 52 insertions, 29 deletions
diff --git a/Python/pystate.c b/Python/pystate.c index a94a615..6a15b54 100644 --- a/Python/pystate.c +++ b/Python/pystate.c @@ -3,6 +3,7 @@ #include "Python.h" #include "pycore_ceval.h" +#include "pycore_frame.h" #include "pycore_initconfig.h" #include "pycore_object.h" // _PyType_InitCache() #include "pycore_pyerrors.h" @@ -685,7 +686,7 @@ new_threadstate(PyInterpreterState *interp, int init) PyMem_RawFree(tstate); return NULL; } - /* If top points to entry 0, then _PyThreadState_PopLocals will try to pop this chunk */ + /* If top points to entry 0, then _PyThreadState_PopFrame will try to pop this chunk */ tstate->datastack_top = &tstate->datastack_chunk->data[1]; tstate->datastack_limit = (PyObject **)(((char *)tstate->datastack_chunk) + DATA_STACK_CHUNK_SIZE); /* Mark trace_info as uninitialized */ @@ -872,7 +873,7 @@ PyThreadState_Clear(PyThreadState *tstate) "PyThreadState_Clear: warning: thread still has a frame\n"); } - /* Don't clear tstate->frame: it is a borrowed reference */ + /* Don't clear tstate->pyframe: it is a borrowed reference */ Py_CLEAR(tstate->dict); Py_CLEAR(tstate->async_exc); @@ -1133,7 +1134,13 @@ PyFrameObject* PyThreadState_GetFrame(PyThreadState *tstate) { assert(tstate != NULL); - PyFrameObject *frame = tstate->frame; + if (tstate->frame == NULL) { + return NULL; + } + PyFrameObject *frame = _PyFrame_GetFrameObject(tstate->frame); + if (frame == NULL) { + PyErr_Clear(); + } Py_XINCREF(frame); return frame; } @@ -1254,7 +1261,7 @@ _PyThread_CurrentFrames(void) for (i = runtime->interpreters.head; i != NULL; i = i->next) { PyThreadState *t; for (t = i->tstate_head; t != NULL; t = t->next) { - PyFrameObject *frame = t->frame; + InterpreterFrame *frame = t->frame; if (frame == NULL) { continue; } @@ -1262,7 +1269,7 @@ _PyThread_CurrentFrames(void) if (id == NULL) { goto fail; } - int stat = PyDict_SetItem(result, id, (PyObject *)frame); + int stat = PyDict_SetItem(result, id, (PyObject *)_PyFrame_GetFrameObject(frame)); Py_DECREF(id); if (stat < 0) { goto fail; @@ -2009,42 +2016,58 @@ _Py_GetConfig(void) #define MINIMUM_OVERHEAD 1000 -PyObject ** -_PyThreadState_PushLocals(PyThreadState *tstate, int size) +static PyObject ** +push_chunk(PyThreadState *tstate, int size) +{ + assert(tstate->datastack_top + size >= tstate->datastack_limit); + + int allocate_size = DATA_STACK_CHUNK_SIZE; + while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) { + allocate_size *= 2; + } + _PyStackChunk *new = allocate_chunk(allocate_size, tstate->datastack_chunk); + if (new == NULL) { + return NULL; + } + tstate->datastack_chunk->top = tstate->datastack_top - &tstate->datastack_chunk->data[0]; + tstate->datastack_chunk = new; + tstate->datastack_limit = (PyObject **)(((char *)new) + allocate_size); + PyObject **res = &new->data[0]; + tstate->datastack_top = res + size; + return res; +} + +InterpreterFrame * +_PyThreadState_PushFrame(PyThreadState *tstate, PyFrameConstructor *con, PyObject *locals) { - assert(((unsigned)size) < INT_MAX/sizeof(PyObject*)/2); - PyObject **res = tstate->datastack_top; - PyObject **top = res + size; + PyCodeObject *code = (PyCodeObject *)con->fc_code; + int nlocalsplus = code->co_nlocalsplus; + size_t size = nlocalsplus + code->co_stacksize + + FRAME_SPECIALS_SIZE; + assert(size < INT_MAX/sizeof(PyObject *)); + PyObject **localsarray = tstate->datastack_top; + PyObject **top = localsarray + size; if (top >= tstate->datastack_limit) { - int allocate_size = DATA_STACK_CHUNK_SIZE; - while (allocate_size < (int)sizeof(PyObject*)*(size + MINIMUM_OVERHEAD)) { - allocate_size *= 2; - } - _PyStackChunk *new = allocate_chunk(allocate_size, tstate->datastack_chunk); - if (new == NULL) { - goto error; + localsarray = push_chunk(tstate, (int)size); + if (localsarray == NULL) { + return NULL; } - tstate->datastack_chunk->top = tstate->datastack_top - &tstate->datastack_chunk->data[0]; - tstate->datastack_chunk = new; - tstate->datastack_limit = (PyObject **)(((char *)new) + allocate_size); - res = &new->data[0]; - tstate->datastack_top = res + size; } else { tstate->datastack_top = top; } - for (int i=0; i < size; i++) { - res[i] = NULL; + InterpreterFrame * frame = (InterpreterFrame *)(localsarray + nlocalsplus); + _PyFrame_InitializeSpecials(frame, con, locals, nlocalsplus); + for (int i=0; i < nlocalsplus; i++) { + localsarray[i] = NULL; } - return res; -error: - _PyErr_SetString(tstate, PyExc_MemoryError, "Out of memory"); - return NULL; + return frame; } void -_PyThreadState_PopLocals(PyThreadState *tstate, PyObject **locals) +_PyThreadState_PopFrame(PyThreadState *tstate, InterpreterFrame * frame) { + PyObject **locals = _PyFrame_GetLocalsArray(frame); if (locals == &tstate->datastack_chunk->data[0]) { _PyStackChunk *chunk = tstate->datastack_chunk; _PyStackChunk *previous = chunk->previous; |