summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>2019-06-17 01:28:43 -0700
committerGitHub <noreply@github.com>2019-06-17 01:28:43 -0700
commit351b0e793e35510e8cbbcbb455a1b9544e808cdd (patch)
tree07768052945be620db0a891dbede0c2a9150171f
parentb101fa7783615051a89500e488708b955eac94c5 (diff)
downloadcpython-git-351b0e793e35510e8cbbcbb455a1b9544e808cdd.tar.gz
bpo-36688: Adding an implementation of RLock in _dummy_thread (GH-12943)
(cherry picked from commit c5905f39bcf4ef895d42eede41bb5a2f071a501d) Co-authored-by: Joost Lek <vlabakje@gmail.com>
-rw-r--r--Lib/_dummy_thread.py32
-rw-r--r--Lib/test/test_dummy_thread.py21
2 files changed, 52 insertions, 1 deletions
diff --git a/Lib/_dummy_thread.py b/Lib/_dummy_thread.py
index a2cae54b05..2e46a07603 100644
--- a/Lib/_dummy_thread.py
+++ b/Lib/_dummy_thread.py
@@ -14,7 +14,7 @@ Suggested usage is::
# Exports only things specified by thread documentation;
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
- 'interrupt_main', 'LockType']
+ 'interrupt_main', 'LockType', 'RLock']
# A dummy value
TIMEOUT_MAX = 2**31
@@ -148,6 +148,36 @@ class LockType(object):
hex(id(self))
)
+
+class RLock(LockType):
+ """Dummy implementation of threading._RLock.
+
+ Re-entrant lock can be aquired multiple times and needs to be released
+ just as many times. This dummy implemention does not check wheter the
+ current thread actually owns the lock, but does accounting on the call
+ counts.
+ """
+ def __init__(self):
+ super().__init__()
+ self._levels = 0
+
+ def acquire(self, waitflag=None, timeout=-1):
+ """Aquire the lock, can be called multiple times in succession.
+ """
+ locked = super().acquire(waitflag, timeout)
+ if locked:
+ self._levels += 1
+ return locked
+
+ def release(self):
+ """Release needs to be called once for every call to acquire().
+ """
+ if self._levels == 0:
+ raise error
+ if self._levels == 1:
+ super().release()
+ self._levels -= 1
+
# Used to signal that interrupt_main was called in a "thread"
_interrupt = False
# True when not executing in a "thread"
diff --git a/Lib/test/test_dummy_thread.py b/Lib/test/test_dummy_thread.py
index da51216783..0f56fcf973 100644
--- a/Lib/test/test_dummy_thread.py
+++ b/Lib/test/test_dummy_thread.py
@@ -102,6 +102,24 @@ class LockTests(unittest.TestCase):
self.assertIn("unlocked", repr(self.lock))
+class RLockTests(unittest.TestCase):
+ """Test dummy RLock objects."""
+
+ def setUp(self):
+ self.rlock = _thread.RLock()
+
+ def test_multiple_acquire(self):
+ self.assertIn("unlocked", repr(self.rlock))
+ self.rlock.acquire()
+ self.rlock.acquire()
+ self.assertIn("locked", repr(self.rlock))
+ self.rlock.release()
+ self.assertIn("locked", repr(self.rlock))
+ self.rlock.release()
+ self.assertIn("unlocked", repr(self.rlock))
+ self.assertRaises(RuntimeError, self.rlock.release)
+
+
class MiscTests(unittest.TestCase):
"""Miscellaneous tests."""
@@ -253,3 +271,6 @@ class ThreadTests(unittest.TestCase):
func = mock.Mock(side_effect=Exception)
_thread.start_new_thread(func, tuple())
self.assertTrue(mock_print_exc.called)
+
+if __name__ == '__main__':
+ unittest.main()