From ced49a196f0e1ea06ba892b875f18e7b11ed26b7 Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Thu, 22 May 2025 16:14:53 +0200 Subject: [3.14] gh-134323: Fix the new `threading.RLock.locked` method (GH-134368) (#134510) gh-134323: Fix the new `threading.RLock.locked` method (GH-134368) (cherry picked from commit 3effede97cc13fc0c5ab5dcde26cc319f388e84c) Co-authored-by: Duprat Co-authored-by: Kumar Aditya --- Lib/test/lock_tests.py | 18 ++++++++++++++++++ Lib/threading.py | 4 ++-- .../2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst | 1 + Modules/_threadmodule.c | 10 ++++++++-- 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst diff --git a/Lib/test/lock_tests.py b/Lib/test/lock_tests.py index 009e04e..0e8c75f 100644 --- a/Lib/test/lock_tests.py +++ b/Lib/test/lock_tests.py @@ -365,6 +365,24 @@ class RLockTests(BaseLockTests): lock.release() self.assertFalse(lock.locked()) + def test_locked_with_2threads(self): + # see gh-134323: check that a rlock which + # is acquired in a different thread, + # is still locked in the main thread. + result = [] + rlock = self.locktype() + self.assertFalse(rlock.locked()) + def acquire(): + result.append(rlock.locked()) + rlock.acquire() + result.append(rlock.locked()) + + with Bunch(acquire, 1): + pass + self.assertTrue(rlock.locked()) + self.assertFalse(result[0]) + self.assertTrue(result[1]) + def test_release_save_unacquired(self): # Cannot _release_save an unacquired lock lock = self.locktype() diff --git a/Lib/threading.py b/Lib/threading.py index fa290d1..2a65f9a 100644 --- a/Lib/threading.py +++ b/Lib/threading.py @@ -165,7 +165,7 @@ class _RLock: except KeyError: pass return "<%s %s.%s object owner=%r count=%d at %s>" % ( - "locked" if self._block.locked() else "unlocked", + "locked" if self.locked() else "unlocked", self.__class__.__module__, self.__class__.__qualname__, owner, @@ -244,7 +244,7 @@ class _RLock: def locked(self): """Return whether this object is locked.""" - return self._count > 0 + return self._block.locked() # Internal methods used by condition variables diff --git a/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst b/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst new file mode 100644 index 0000000..7982b52 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-05-20-19-16-30.gh-issue-134323.ZQZGvw.rst @@ -0,0 +1 @@ +Fix the :meth:`threading.RLock.locked` method. diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c index 9776a32..8bd59ff 100644 --- a/Modules/_threadmodule.c +++ b/Modules/_threadmodule.c @@ -1011,6 +1011,11 @@ rlock_traverse(PyObject *self, visitproc visit, void *arg) return 0; } +static int +rlock_locked_impl(rlockobject *self) +{ + return PyMutex_IsLocked(&self->lock.mutex); +} static void rlock_dealloc(PyObject *self) @@ -1100,7 +1105,7 @@ static PyObject * rlock_locked(PyObject *op, PyObject *Py_UNUSED(ignored)) { rlockobject *self = rlockobject_CAST(op); - int is_locked = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock); + int is_locked = rlock_locked_impl(self); return PyBool_FromLong(is_locked); } @@ -1202,10 +1207,11 @@ rlock_repr(PyObject *op) { rlockobject *self = rlockobject_CAST(op); PyThread_ident_t owner = self->lock.thread; + int locked = rlock_locked_impl(self); size_t count = self->lock.level + 1; return PyUnicode_FromFormat( "<%s %s object owner=%" PY_FORMAT_THREAD_IDENT_T " count=%zu at %p>", - owner ? "locked" : "unlocked", + locked ? "locked" : "unlocked", Py_TYPE(self)->tp_name, owner, count, self); } -- cgit v0.12