diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-10-16-17-55-45.gh-issue-159558.ftCsRe.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-16-17-55-45.gh-issue-159558.ftCsRe.rst new file mode 100644 index 00000000000000..db469b159e023f --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-10-16-17-55-45.gh-issue-159558.ftCsRe.rst @@ -0,0 +1,3 @@ +Speed up ``set >= set``, ``set > set``, :meth:`set.issuperset`, :meth:`set.discard` +and :meth:`set.remove` on the free-threaded build by not re-entering a critical +section already held on the set. diff --git a/Objects/setobject.c b/Objects/setobject.c index 8fdd1eb26118c0..e279a90d4483dc 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -411,7 +411,8 @@ set_insert_clean(setentry *table, size_t mask, PyObject *key, Py_hash_t hash) /* ======================================================================== */ static int -set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash, setentry **epp) +set_lookkey_lock_held(PySetObject *so, PyObject *key, Py_hash_t hash, + setentry **epp) { int status; if (PyFrozenSet_CheckExact(so)) { @@ -419,12 +420,10 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash, setentry **epp) set_compare_frozenset); } else { - Py_BEGIN_CRITICAL_SECTION(so); do { status = set_do_lookup(so, so->table, so->mask, key, hash, epp, set_compare_entry_lock_held); } while (status == SET_LOOKKEY_CHANGED); - Py_END_CRITICAL_SECTION(); } assert(status == SET_LOOKKEY_FOUND || status == SET_LOOKKEY_NO_MATCH || @@ -432,6 +431,19 @@ set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash, setentry **epp) return status; } +static int +set_lookkey(PySetObject *so, PyObject *key, Py_hash_t hash, setentry **epp) +{ + if (PyFrozenSet_CheckExact(so)) { + return set_lookkey_lock_held(so, key, hash, epp); + } + int status; + Py_BEGIN_CRITICAL_SECTION(so); + status = set_lookkey_lock_held(so, key, hash, epp); + Py_END_CRITICAL_SECTION(); + return status; +} + #ifdef Py_GIL_DISABLED static int set_lookkey_threadsafe(PySetObject *so, PyObject *key, Py_hash_t hash) @@ -581,7 +593,7 @@ set_discard_entry(PySetObject *so, PyObject *key, Py_hash_t hash) { setentry *entry; PyObject *old_key; - int status = set_lookkey(so, key, hash, &entry); + int status = set_lookkey_lock_held(so, key, hash, &entry); if (status < 0) { return -1; } @@ -2418,6 +2430,9 @@ set_issubset_impl(PySetObject *so, PyObject *other) Py_ssize_t pos = 0; int rv; + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(so); + _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(other); + if (!PyAnySet_Check(other)) { PyObject *tmp = set_intersection(so, other); if (tmp == NULL) { @@ -2460,7 +2475,7 @@ set_issuperset_impl(PySetObject *so, PyObject *other) /*[clinic end generated code: output=ecf00ce552c09461 input=5f2e1f262e6e4ccc]*/ { if (PyAnySet_Check(other)) { - return set_issubset(other, (PyObject *)so); + return set_issubset_impl((PySetObject *)other, (PyObject *)so); } PyObject *key, *it = PyObject_GetIter(other);