mirror of
https://github.com/python/cpython.git
synced 2025-07-07 19:35:27 +00:00
gh-115942: Add locked
to several multiprocessing locks (#115944)
Co-authored-by: mpage <mpage@cs.stanford.edu> Co-authored-by: Hugo van Kemenade <1324225+hugovk@users.noreply.github.com>
This commit is contained in:
parent
6cd1d6c6b1
commit
f7305a06c7
10 changed files with 89 additions and 6 deletions
|
@ -1421,6 +1421,13 @@ object -- see :ref:`multiprocessing-managers`.
|
||||||
when invoked on an unlocked lock, a :exc:`ValueError` is raised.
|
when invoked on an unlocked lock, a :exc:`ValueError` is raised.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: locked()
|
||||||
|
|
||||||
|
Return a boolean indicating whether this object is locked right now.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
|
||||||
.. class:: RLock()
|
.. class:: RLock()
|
||||||
|
|
||||||
A recursive lock object: a close analog of :class:`threading.RLock`. A
|
A recursive lock object: a close analog of :class:`threading.RLock`. A
|
||||||
|
@ -1481,6 +1488,13 @@ object -- see :ref:`multiprocessing-managers`.
|
||||||
differs from the implemented behavior in :meth:`threading.RLock.release`.
|
differs from the implemented behavior in :meth:`threading.RLock.release`.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: locked()
|
||||||
|
|
||||||
|
Return a boolean indicating whether this object is locked right now.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
|
||||||
.. class:: Semaphore([value])
|
.. class:: Semaphore([value])
|
||||||
|
|
||||||
A semaphore object: a close analog of :class:`threading.Semaphore`.
|
A semaphore object: a close analog of :class:`threading.Semaphore`.
|
||||||
|
|
|
@ -709,6 +709,13 @@ call release as many times the lock has been acquired can lead to deadlock.
|
||||||
There is no return value.
|
There is no return value.
|
||||||
|
|
||||||
|
|
||||||
|
.. method:: locked()
|
||||||
|
|
||||||
|
Return a boolean indicating whether this object is locked right now.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
|
|
||||||
.. _condition-objects:
|
.. _condition-objects:
|
||||||
|
|
||||||
Condition Objects
|
Condition Objects
|
||||||
|
@ -801,6 +808,12 @@ item to the buffer only needs to wake up one consumer thread.
|
||||||
Release the underlying lock. This method calls the corresponding method on
|
Release the underlying lock. This method calls the corresponding method on
|
||||||
the underlying lock; there is no return value.
|
the underlying lock; there is no return value.
|
||||||
|
|
||||||
|
.. method:: locked()
|
||||||
|
|
||||||
|
Return a boolean indicating whether this object is locked right now.
|
||||||
|
|
||||||
|
.. versionadded:: next
|
||||||
|
|
||||||
.. method:: wait(timeout=None)
|
.. method:: wait(timeout=None)
|
||||||
|
|
||||||
Wait until notified or until a timeout occurs. If the calling thread has
|
Wait until notified or until a timeout occurs. If the calling thread has
|
||||||
|
|
|
@ -382,6 +382,9 @@ class _ModuleLock:
|
||||||
self.waiters.pop()
|
self.waiters.pop()
|
||||||
self.wakeup.release()
|
self.wakeup.release()
|
||||||
|
|
||||||
|
def locked(self):
|
||||||
|
return bool(self.count)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f'_ModuleLock({self.name!r}) at {id(self)}'
|
return f'_ModuleLock({self.name!r}) at {id(self)}'
|
||||||
|
|
||||||
|
|
|
@ -1059,12 +1059,14 @@ class IteratorProxy(BaseProxy):
|
||||||
|
|
||||||
|
|
||||||
class AcquirerProxy(BaseProxy):
|
class AcquirerProxy(BaseProxy):
|
||||||
_exposed_ = ('acquire', 'release')
|
_exposed_ = ('acquire', 'release', 'locked')
|
||||||
def acquire(self, blocking=True, timeout=None):
|
def acquire(self, blocking=True, timeout=None):
|
||||||
args = (blocking,) if timeout is None else (blocking, timeout)
|
args = (blocking,) if timeout is None else (blocking, timeout)
|
||||||
return self._callmethod('acquire', args)
|
return self._callmethod('acquire', args)
|
||||||
def release(self):
|
def release(self):
|
||||||
return self._callmethod('release')
|
return self._callmethod('release')
|
||||||
|
def locked(self):
|
||||||
|
return self._callmethod('locked')
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self._callmethod('acquire')
|
return self._callmethod('acquire')
|
||||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
@ -1072,7 +1074,7 @@ class AcquirerProxy(BaseProxy):
|
||||||
|
|
||||||
|
|
||||||
class ConditionProxy(AcquirerProxy):
|
class ConditionProxy(AcquirerProxy):
|
||||||
_exposed_ = ('acquire', 'release', 'wait', 'notify', 'notify_all')
|
_exposed_ = ('acquire', 'release', 'locked', 'wait', 'notify', 'notify_all')
|
||||||
def wait(self, timeout=None):
|
def wait(self, timeout=None):
|
||||||
return self._callmethod('wait', (timeout,))
|
return self._callmethod('wait', (timeout,))
|
||||||
def notify(self, n=1):
|
def notify(self, n=1):
|
||||||
|
|
|
@ -90,6 +90,9 @@ class SemLock(object):
|
||||||
self.acquire = self._semlock.acquire
|
self.acquire = self._semlock.acquire
|
||||||
self.release = self._semlock.release
|
self.release = self._semlock.release
|
||||||
|
|
||||||
|
def locked(self):
|
||||||
|
return self._semlock._count() != 0
|
||||||
|
|
||||||
def __enter__(self):
|
def __enter__(self):
|
||||||
return self._semlock.__enter__()
|
return self._semlock.__enter__()
|
||||||
|
|
||||||
|
|
|
@ -1486,8 +1486,10 @@ class _TestLock(BaseTestCase):
|
||||||
def test_lock(self):
|
def test_lock(self):
|
||||||
lock = self.Lock()
|
lock = self.Lock()
|
||||||
self.assertEqual(lock.acquire(), True)
|
self.assertEqual(lock.acquire(), True)
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
self.assertEqual(lock.acquire(False), False)
|
self.assertEqual(lock.acquire(False), False)
|
||||||
self.assertEqual(lock.release(), None)
|
self.assertEqual(lock.release(), None)
|
||||||
|
self.assertFalse(lock.locked())
|
||||||
self.assertRaises((ValueError, threading.ThreadError), lock.release)
|
self.assertRaises((ValueError, threading.ThreadError), lock.release)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -1549,16 +1551,23 @@ class _TestLock(BaseTestCase):
|
||||||
def test_rlock(self):
|
def test_rlock(self):
|
||||||
lock = self.RLock()
|
lock = self.RLock()
|
||||||
self.assertEqual(lock.acquire(), True)
|
self.assertEqual(lock.acquire(), True)
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
self.assertEqual(lock.acquire(), True)
|
self.assertEqual(lock.acquire(), True)
|
||||||
self.assertEqual(lock.acquire(), True)
|
self.assertEqual(lock.acquire(), True)
|
||||||
self.assertEqual(lock.release(), None)
|
self.assertEqual(lock.release(), None)
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
self.assertEqual(lock.release(), None)
|
self.assertEqual(lock.release(), None)
|
||||||
self.assertEqual(lock.release(), None)
|
self.assertEqual(lock.release(), None)
|
||||||
|
self.assertFalse(lock.locked())
|
||||||
self.assertRaises((AssertionError, RuntimeError), lock.release)
|
self.assertRaises((AssertionError, RuntimeError), lock.release)
|
||||||
|
|
||||||
def test_lock_context(self):
|
def test_lock_context(self):
|
||||||
with self.Lock():
|
with self.Lock() as locked:
|
||||||
pass
|
self.assertTrue(locked)
|
||||||
|
|
||||||
|
def test_rlock_context(self):
|
||||||
|
with self.RLock() as locked:
|
||||||
|
self.assertTrue(locked)
|
||||||
|
|
||||||
|
|
||||||
class _TestSemaphore(BaseTestCase):
|
class _TestSemaphore(BaseTestCase):
|
||||||
|
@ -6254,6 +6263,7 @@ class TestSyncManagerTypes(unittest.TestCase):
|
||||||
@classmethod
|
@classmethod
|
||||||
def _test_lock(cls, obj):
|
def _test_lock(cls, obj):
|
||||||
obj.acquire()
|
obj.acquire()
|
||||||
|
obj.locked()
|
||||||
|
|
||||||
def test_lock(self, lname="Lock"):
|
def test_lock(self, lname="Lock"):
|
||||||
o = getattr(self.manager, lname)()
|
o = getattr(self.manager, lname)()
|
||||||
|
@ -6265,8 +6275,9 @@ class TestSyncManagerTypes(unittest.TestCase):
|
||||||
def _test_rlock(cls, obj):
|
def _test_rlock(cls, obj):
|
||||||
obj.acquire()
|
obj.acquire()
|
||||||
obj.release()
|
obj.release()
|
||||||
|
obj.locked()
|
||||||
|
|
||||||
def test_rlock(self, lname="Lock"):
|
def test_rlock(self, lname="RLock"):
|
||||||
o = getattr(self.manager, lname)()
|
o = getattr(self.manager, lname)()
|
||||||
self.run_worker(self._test_rlock, o)
|
self.run_worker(self._test_rlock, o)
|
||||||
|
|
||||||
|
|
|
@ -353,6 +353,18 @@ class RLockTests(BaseLockTests):
|
||||||
lock.release()
|
lock.release()
|
||||||
self.assertRaises(RuntimeError, lock.release)
|
self.assertRaises(RuntimeError, lock.release)
|
||||||
|
|
||||||
|
def test_locked(self):
|
||||||
|
lock = self.locktype()
|
||||||
|
self.assertFalse(lock.locked())
|
||||||
|
lock.acquire()
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
|
lock.acquire()
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
|
lock.release()
|
||||||
|
self.assertTrue(lock.locked())
|
||||||
|
lock.release()
|
||||||
|
self.assertFalse(lock.locked())
|
||||||
|
|
||||||
def test_release_save_unacquired(self):
|
def test_release_save_unacquired(self):
|
||||||
# Cannot _release_save an unacquired lock
|
# Cannot _release_save an unacquired lock
|
||||||
lock = self.locktype()
|
lock = self.locktype()
|
||||||
|
|
|
@ -241,6 +241,10 @@ class _RLock:
|
||||||
def __exit__(self, t, v, tb):
|
def __exit__(self, t, v, tb):
|
||||||
self.release()
|
self.release()
|
||||||
|
|
||||||
|
def locked(self):
|
||||||
|
"""Return whether this object is locked."""
|
||||||
|
return self._count > 0
|
||||||
|
|
||||||
# Internal methods used by condition variables
|
# Internal methods used by condition variables
|
||||||
|
|
||||||
def _acquire_restore(self, state):
|
def _acquire_restore(self, state):
|
||||||
|
@ -286,9 +290,10 @@ class Condition:
|
||||||
if lock is None:
|
if lock is None:
|
||||||
lock = RLock()
|
lock = RLock()
|
||||||
self._lock = lock
|
self._lock = lock
|
||||||
# Export the lock's acquire() and release() methods
|
# Export the lock's acquire(), release(), and locked() methods
|
||||||
self.acquire = lock.acquire
|
self.acquire = lock.acquire
|
||||||
self.release = lock.release
|
self.release = lock.release
|
||||||
|
self.locked = lock.locked
|
||||||
# If the lock defines _release_save() and/or _acquire_restore(),
|
# If the lock defines _release_save() and/or _acquire_restore(),
|
||||||
# these override the default implementations (which just call
|
# these override the default implementations (which just call
|
||||||
# release() and acquire() on the lock). Ditto for _is_owned().
|
# release() and acquire() on the lock). Ditto for _is_owned().
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
Add :meth:`threading.RLock.locked`,
|
||||||
|
:meth:`multiprocessing.Lock.locked`,
|
||||||
|
:meth:`multiprocessing.RLock.locked`,
|
||||||
|
and allow :meth:`multiprocessing.managers.SyncManager.Lock` and
|
||||||
|
:meth:`multiprocessing.managers.SyncManager.RLock` to proxy ``locked()`` call.
|
|
@ -1086,6 +1086,19 @@ PyDoc_STRVAR(rlock_exit_doc,
|
||||||
\n\
|
\n\
|
||||||
Release the lock.");
|
Release the lock.");
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
rlock_locked(PyObject *op, PyObject *Py_UNUSED(ignored))
|
||||||
|
{
|
||||||
|
rlockobject *self = rlockobject_CAST(op);
|
||||||
|
int is_locked = _PyRecursiveMutex_IsLockedByCurrentThread(&self->lock);
|
||||||
|
return PyBool_FromLong(is_locked);
|
||||||
|
}
|
||||||
|
|
||||||
|
PyDoc_STRVAR(rlock_locked_doc,
|
||||||
|
"locked()\n\
|
||||||
|
\n\
|
||||||
|
Return a boolean indicating whether this object is locked right now.");
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
rlock_acquire_restore(PyObject *op, PyObject *args)
|
rlock_acquire_restore(PyObject *op, PyObject *args)
|
||||||
{
|
{
|
||||||
|
@ -1204,6 +1217,8 @@ static PyMethodDef rlock_methods[] = {
|
||||||
METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
|
METH_VARARGS | METH_KEYWORDS, rlock_acquire_doc},
|
||||||
{"release", rlock_release,
|
{"release", rlock_release,
|
||||||
METH_NOARGS, rlock_release_doc},
|
METH_NOARGS, rlock_release_doc},
|
||||||
|
{"locked", rlock_locked,
|
||||||
|
METH_NOARGS, rlock_locked_doc},
|
||||||
{"_is_owned", rlock_is_owned,
|
{"_is_owned", rlock_is_owned,
|
||||||
METH_NOARGS, rlock_is_owned_doc},
|
METH_NOARGS, rlock_is_owned_doc},
|
||||||
{"_acquire_restore", rlock_acquire_restore,
|
{"_acquire_restore", rlock_acquire_restore,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue