]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-140476: optimize `PySet_Add` for `frozenset` in free-threading (#140440)
authorAlper <alperyoney@fb.com>
Tue, 11 Nov 2025 20:27:21 +0000 (12:27 -0800)
committerGitHub <noreply@github.com>
Tue, 11 Nov 2025 20:27:21 +0000 (01:57 +0530)
Avoids critical section in `PySet_Add` when adding items to newly created frozensets.

Co-authored-by: Kumar Aditya <kumaraditya@python.org>
Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-12-48-05.gh-issue-140476.F3-d1P.rst [new file with mode: 0644]
Objects/setobject.c

diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-12-48-05.gh-issue-140476.F3-d1P.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-10-22-12-48-05.gh-issue-140476.F3-d1P.rst
new file mode 100644 (file)
index 0000000..a240332
--- /dev/null
@@ -0,0 +1,2 @@
+Optimize :c:func:`PySet_Add` for :class:`frozenset` in :term:`free threaded
+<free threading>` build.
index 2401176576eb62b415893a69e0135bcc3c46fc09..85f4d7d403178adbeb53bace11b41861eeb9f7f3 100644 (file)
@@ -2775,17 +2775,24 @@ PySet_Discard(PyObject *set, PyObject *key)
 int
 PySet_Add(PyObject *anyset, PyObject *key)
 {
-    if (!PySet_Check(anyset) &&
-        (!PyFrozenSet_Check(anyset) || !_PyObject_IsUniquelyReferenced(anyset))) {
-        PyErr_BadInternalCall();
-        return -1;
+    if (PySet_Check(anyset)) {
+        int rv;
+        Py_BEGIN_CRITICAL_SECTION(anyset);
+        rv = set_add_key((PySetObject *)anyset, key);
+        Py_END_CRITICAL_SECTION();
+        return rv;
     }
 
-    int rv;
-    Py_BEGIN_CRITICAL_SECTION(anyset);
-    rv = set_add_key((PySetObject *)anyset, key);
-    Py_END_CRITICAL_SECTION();
-    return rv;
+    if (PyFrozenSet_Check(anyset) && _PyObject_IsUniquelyReferenced(anyset)) {
+        // We can only change frozensets if they are uniquely referenced. The
+        // API limits the usage of `PySet_Add` to "fill in the values of brand
+        // new frozensets before they are exposed to other code". In this case,
+        // this can be done without a lock.
+        return set_add_key((PySetObject *)anyset, key);
+    }
+
+    PyErr_BadInternalCall();
+    return -1;
 }
 
 int