gh-123923: Defer refcounting for f_funcobj in _PyInterpreterFrame (#124026)

Use a `_PyStackRef` and defer the reference to `f_funcobj` when
possible. This avoids some reference count contention in the common case
of executing the same code object from multiple threads concurrently in
the free-threaded build.
This commit is contained in:
Sam Gross 2024-09-24 13:08:18 -07:00 committed by GitHub
parent d3c76dff44
commit f4997bb3ac
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 143 additions and 137 deletions

View file

@ -1634,7 +1634,7 @@ frame_dealloc(PyFrameObject *f)
/* Kill all local variables including specials, if we own them */
if (f->f_frame == frame && frame->owner == FRAME_OWNED_BY_FRAME_OBJECT) {
PyStackRef_CLEAR(frame->f_executable);
Py_CLEAR(frame->f_funcobj);
PyStackRef_CLEAR(frame->f_funcobj);
Py_CLEAR(frame->f_locals);
_PyStackRef *locals = _PyFrame_GetLocalsArray(frame);
_PyStackRef *sp = frame->stackpointer;
@ -1790,7 +1790,7 @@ static void
init_frame(_PyInterpreterFrame *frame, PyFunctionObject *func, PyObject *locals)
{
PyCodeObject *code = (PyCodeObject *)func->func_code;
_PyFrame_Initialize(frame, (PyFunctionObject*)Py_NewRef(func),
_PyFrame_Initialize(frame, PyStackRef_FromPyObjectNew(func),
Py_XNewRef(locals), code, 0, NULL);
}
@ -1861,14 +1861,15 @@ frame_init_get_vars(_PyInterpreterFrame *frame)
PyCodeObject *co = _PyFrame_GetCode(frame);
int lasti = _PyInterpreterFrame_LASTI(frame);
if (!(lasti < 0 && _PyCode_CODE(co)->op.code == COPY_FREE_VARS
&& PyFunction_Check(frame->f_funcobj)))
&& PyStackRef_FunctionCheck(frame->f_funcobj)))
{
/* Free vars are initialized */
return;
}
/* Free vars have not been initialized -- Do that */
PyObject *closure = ((PyFunctionObject *)frame->f_funcobj)->func_closure;
PyFunctionObject *func = _PyFrame_GetFunction(frame);
PyObject *closure = func->func_closure;
int offset = PyUnstable_Code_GetFirstFree(co);
for (int i = 0; i < co->co_nfreevars; ++i) {
PyObject *o = PyTuple_GET_ITEM(closure, i);