Skip to content

Commit 351b0e7

Browse files
bpo-36688: Adding an implementation of RLock in _dummy_thread (GH-12943)
(cherry picked from commit c5905f3) Co-authored-by: Joost Lek <[email protected]>
1 parent b101fa7 commit 351b0e7

2 files changed

Lines changed: 52 additions & 1 deletion

File tree

Lib/_dummy_thread.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
# Exports only things specified by thread documentation;
1515
# skipping obsolete synonyms allocate(), start_new(), exit_thread().
1616
__all__ = ['error', 'start_new_thread', 'exit', 'get_ident', 'allocate_lock',
17-
'interrupt_main', 'LockType']
17+
'interrupt_main', 'LockType', 'RLock']
1818

1919
# A dummy value
2020
TIMEOUT_MAX = 2**31
@@ -148,6 +148,36 @@ def __repr__(self):
148148
hex(id(self))
149149
)
150150

151+
152+
class RLock(LockType):
153+
"""Dummy implementation of threading._RLock.
154+
155+
Re-entrant lock can be aquired multiple times and needs to be released
156+
just as many times. This dummy implemention does not check wheter the
157+
current thread actually owns the lock, but does accounting on the call
158+
counts.
159+
"""
160+
def __init__(self):
161+
super().__init__()
162+
self._levels = 0
163+
164+
def acquire(self, waitflag=None, timeout=-1):
165+
"""Aquire the lock, can be called multiple times in succession.
166+
"""
167+
locked = super().acquire(waitflag, timeout)
168+
if locked:
169+
self._levels += 1
170+
return locked
171+
172+
def release(self):
173+
"""Release needs to be called once for every call to acquire().
174+
"""
175+
if self._levels == 0:
176+
raise error
177+
if self._levels == 1:
178+
super().release()
179+
self._levels -= 1
180+
151181
# Used to signal that interrupt_main was called in a "thread"
152182
_interrupt = False
153183
# True when not executing in a "thread"

Lib/test/test_dummy_thread.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,24 @@ def test_lock_representation(self):
102102
self.assertIn("unlocked", repr(self.lock))
103103

104104

105+
class RLockTests(unittest.TestCase):
106+
"""Test dummy RLock objects."""
107+
108+
def setUp(self):
109+
self.rlock = _thread.RLock()
110+
111+
def test_multiple_acquire(self):
112+
self.assertIn("unlocked", repr(self.rlock))
113+
self.rlock.acquire()
114+
self.rlock.acquire()
115+
self.assertIn("locked", repr(self.rlock))
116+
self.rlock.release()
117+
self.assertIn("locked", repr(self.rlock))
118+
self.rlock.release()
119+
self.assertIn("unlocked", repr(self.rlock))
120+
self.assertRaises(RuntimeError, self.rlock.release)
121+
122+
105123
class MiscTests(unittest.TestCase):
106124
"""Miscellaneous tests."""
107125

@@ -253,3 +271,6 @@ def test_RaiseException(self, mock_print_exc):
253271
func = mock.Mock(side_effect=Exception)
254272
_thread.start_new_thread(func, tuple())
255273
self.assertTrue(mock_print_exc.called)
274+
275+
if __name__ == '__main__':
276+
unittest.main()

0 commit comments

Comments
 (0)