From 7217ad07daa0a404fcf4452d8cf44fabb2b7c1de Mon Sep 17 00:00:00 2001 From: Daniele Parmeggiani Date: Fri, 27 Feb 2026 16:21:11 +0000 Subject: [PATCH 1/2] gh-135871: Reload lock internal state while spinning in `_PyMutex_LockTimed` --- Python/lock.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/Python/lock.c b/Python/lock.c index ad97bfd93c8495..03bdc21b70f230 100644 --- a/Python/lock.c +++ b/Python/lock.c @@ -27,8 +27,10 @@ static const PyTime_t TIME_TO_BE_FAIR_NS = 1000*1000; // enabled. #if Py_GIL_DISABLED static const int MAX_SPIN_COUNT = 40; +static const int RELOAD_SPIN_COUNT = 3; #else static const int MAX_SPIN_COUNT = 0; +static const int RELOAD_SPIN_COUNT = 1; #endif struct mutex_entry { @@ -79,6 +81,16 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) }; Py_ssize_t spin_count = 0; +#ifdef Py_GIL_DISABLED + // Using thread-id as a way of reducing contention further in the reload below. + // It adds a pseudo-random starting offset to the recurrence, so that threads + // are less likely to try and run compare-exchange at the same time. + // The lower bits of platform thread ids are likely to not be random, + // hence the right shift. + const Py_ssize_t tid = (Py_ssize_t) _Py_ThreadId() >> 12; +#else + const Py_ssize_t tid = 0; +#endif for (;;) { if ((v & _Py_LOCKED) == 0) { // The lock is unlocked. Try to grab it. @@ -92,6 +104,9 @@ _PyMutex_LockTimed(PyMutex *m, PyTime_t timeout, _PyLockFlags flags) // Spin for a bit. _Py_yield(); spin_count++; + if (((spin_count + tid) & RELOAD_SPIN_COUNT) == 0) { + v = _Py_atomic_load_uint8_relaxed(&m->_bits); + } continue; } From 54932d638bf3bbe0cac46b0acc1523f64d844307 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 16:16:44 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-03-19-16-16-40.gh-issue-135871.jSExZ3.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2026-03-19-16-16-40.gh-issue-135871.jSExZ3.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-03-19-16-16-40.gh-issue-135871.jSExZ3.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-19-16-16-40.gh-issue-135871.jSExZ3.rst new file mode 100644 index 00000000000000..29103e46906487 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2026-03-19-16-16-40.gh-issue-135871.jSExZ3.rst @@ -0,0 +1 @@ +Improve multithreaded scaling of PyMutex in low-contention scenarios by reloading the lock's internal state, without slowing down high-contention scenarios.