summaryrefslogtreecommitdiffstats
path: root/Include
diff options
context:
space:
mode:
authorSam Gross <colesbury@gmail.com>2024-10-24 16:44:38 (GMT)
committerGitHub <noreply@github.com>2024-10-24 16:44:38 (GMT)
commit3c4a7fa6178d852ccb73527aaa2d0a5e93022e89 (patch)
treee74c8d15c6dab8e3b58478aa5e0d9915477f83de /Include
parent5003ad5c5ea508f0dde1b374cd8bc6a481ad5c5d (diff)
downloadcpython-3c4a7fa6178d852ccb73527aaa2d0a5e93022e89.zip
cpython-3c4a7fa6178d852ccb73527aaa2d0a5e93022e89.tar.gz
cpython-3c4a7fa6178d852ccb73527aaa2d0a5e93022e89.tar.bz2
gh-124218: Avoid refcount contention on builtins module (GH-125847)
This replaces `_PyEval_BuiltinsFromGlobals` with `_PyDict_LoadBuiltinsFromGlobals`, which returns a new reference instead of a borrowed reference. Internally, the new function uses per-thread reference counting when possible to avoid contention on the refcount fields on the builtins module.
Diffstat (limited to 'Include')
-rw-r--r--Include/internal/pycore_ceval.h3
-rw-r--r--Include/internal/pycore_dict.h29
2 files changed, 29 insertions, 3 deletions
diff --git a/Include/internal/pycore_ceval.h b/Include/internal/pycore_ceval.h
index cff2b1f..411bbff 100644
--- a/Include/internal/pycore_ceval.h
+++ b/Include/internal/pycore_ceval.h
@@ -83,9 +83,6 @@ extern void _PyEval_Fini(void);
extern PyObject* _PyEval_GetBuiltins(PyThreadState *tstate);
-extern PyObject* _PyEval_BuiltinsFromGlobals(
- PyThreadState *tstate,
- PyObject *globals);
// Trampoline API
diff --git a/Include/internal/pycore_dict.h b/Include/internal/pycore_dict.h
index 1d18555..c5399ad 100644
--- a/Include/internal/pycore_dict.h
+++ b/Include/internal/pycore_dict.h
@@ -108,6 +108,9 @@ extern Py_ssize_t _PyDictKeys_StringLookup(PyDictKeysObject* dictkeys, PyObject
PyAPI_FUNC(PyObject *)_PyDict_LoadGlobal(PyDictObject *, PyDictObject *, PyObject *);
PyAPI_FUNC(void) _PyDict_LoadGlobalStackRef(PyDictObject *, PyDictObject *, PyObject *, _PyStackRef *);
+// Loads the __builtins__ object from the globals dict. Returns a new reference.
+extern PyObject *_PyDict_LoadBuiltinsFromGlobals(PyObject *globals);
+
/* Consumes references to key and value */
PyAPI_FUNC(int) _PyDict_SetItem_Take2(PyDictObject *op, PyObject *key, PyObject *value);
extern int _PyDict_SetItem_LockHeld(PyDictObject *dict, PyObject *name, PyObject *value);
@@ -318,6 +321,8 @@ PyDictObject *_PyObject_MaterializeManagedDict_LockHeld(PyObject *);
#ifndef Py_GIL_DISABLED
# define _Py_INCREF_DICT Py_INCREF
# define _Py_DECREF_DICT Py_DECREF
+# define _Py_INCREF_BUILTINS Py_INCREF
+# define _Py_DECREF_BUILTINS Py_DECREF
#else
static inline Py_ssize_t
_PyDict_UniqueId(PyDictObject *mp)
@@ -341,6 +346,30 @@ _Py_DECREF_DICT(PyObject *op)
Py_ssize_t id = _PyDict_UniqueId((PyDictObject *)op);
_Py_THREAD_DECREF_OBJECT(op, id);
}
+
+// Like `_Py_INCREF_DICT`, but also handles non-dict objects because builtins
+// may not be a dict.
+static inline void
+_Py_INCREF_BUILTINS(PyObject *op)
+{
+ if (PyDict_CheckExact(op)) {
+ _Py_INCREF_DICT(op);
+ }
+ else {
+ Py_INCREF(op);
+ }
+}
+
+static inline void
+_Py_DECREF_BUILTINS(PyObject *op)
+{
+ if (PyDict_CheckExact(op)) {
+ _Py_DECREF_DICT(op);
+ }
+ else {
+ Py_DECREF(op);
+ }
+}
#endif
#ifdef __cplusplus