summaryrefslogtreecommitdiffstats
path: root/Objects
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2024-09-12 16:37:06 (GMT)
committerGitHub <noreply@github.com>2024-09-12 16:37:06 (GMT)
commitb2afe2aae487ebf89897e22c01d9095944fd334f (patch)
tree3ffa3ebfe3c69cd21968ce76d8d7cb2f325ff6d3 /Objects
parent4ed7d1d6acc22807bfb5983c98fd59f7cb5061db (diff)
downloadcpython-b2afe2aae487ebf89897e22c01d9095944fd334f.zip
cpython-b2afe2aae487ebf89897e22c01d9095944fd334f.tar.gz
cpython-b2afe2aae487ebf89897e22c01d9095944fd334f.tar.bz2
gh-123923: Defer refcounting for `f_executable` in `_PyInterpreterFrame` (#123924)
Use a `_PyStackRef` and defer the reference to `f_executable` 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.
Diffstat (limited to 'Objects')
-rw-r--r--Objects/frameobject.c7
-rw-r--r--Objects/genobject.c16
2 files changed, 13 insertions, 10 deletions
diff --git a/Objects/frameobject.c b/Objects/frameobject.c
index 85c2455..b567327 100644
--- a/Objects/frameobject.c
+++ b/Objects/frameobject.c
@@ -1625,8 +1625,6 @@ frame_dealloc(PyFrameObject *f)
}
Py_TRASHCAN_BEGIN(f, frame_dealloc);
- PyObject *co = NULL;
-
/* GH-106092: If f->f_frame was on the stack and we reached the maximum
* nesting depth for deallocations, the trashcan may have delayed this
* deallocation until after f->f_frame is freed. Avoid dereferencing
@@ -1635,9 +1633,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) {
- /* Don't clear code object until the end */
- co = frame->f_executable;
- frame->f_executable = NULL;
+ PyStackRef_CLEAR(frame->f_executable);
Py_CLEAR(frame->f_funcobj);
Py_CLEAR(frame->f_locals);
_PyStackRef *locals = _PyFrame_GetLocalsArray(frame);
@@ -1652,7 +1648,6 @@ frame_dealloc(PyFrameObject *f)
Py_CLEAR(f->f_extra_locals);
Py_CLEAR(f->f_locals_cache);
PyObject_GC_Del(f);
- Py_XDECREF(co);
Py_TRASHCAN_END;
}
diff --git a/Objects/genobject.c b/Objects/genobject.c
index b281af8..5dc8f92 100644
--- a/Objects/genobject.c
+++ b/Objects/genobject.c
@@ -55,6 +55,14 @@ gen_traverse(PyGenObject *gen, visitproc visit, void *arg)
return err;
}
}
+ else {
+ // We still need to visit the code object when the frame is cleared to
+ // ensure that it's kept alive if the reference is deferred.
+ int err = _PyGC_VisitStackRef(&gen->gi_iframe.f_executable, visit, arg);
+ if (err) {
+ return err;
+ }
+ }
/* No need to visit cr_origin, because it's just tuples/str/int, so can't
participate in a reference cycle. */
Py_VISIT(gen->gi_exc_state.exc_value);
@@ -139,6 +147,9 @@ gen_dealloc(PyGenObject *gen)
and GC_Del. */
Py_CLEAR(((PyAsyncGenObject*)gen)->ag_origin_or_finalizer);
}
+ if (PyCoro_CheckExact(gen)) {
+ Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
+ }
if (gen->gi_frame_state != FRAME_CLEARED) {
_PyInterpreterFrame *frame = &gen->gi_iframe;
gen->gi_frame_state = FRAME_CLEARED;
@@ -147,10 +158,7 @@ gen_dealloc(PyGenObject *gen)
_PyErr_ClearExcState(&gen->gi_exc_state);
}
assert(gen->gi_exc_state.exc_value == NULL);
- if (_PyGen_GetCode(gen)->co_flags & CO_COROUTINE) {
- Py_CLEAR(((PyCoroObject *)gen)->cr_origin_or_finalizer);
- }
- Py_DECREF(_PyGen_GetCode(gen));
+ PyStackRef_CLEAR(gen->gi_iframe.f_executable);
Py_CLEAR(gen->gi_name);
Py_CLEAR(gen->gi_qualname);