Skip to content

Commit a73b47f

Browse files
committed
fix issue 141460
1 parent 16a305f commit a73b47f

File tree

5 files changed

+10
-159
lines changed

5 files changed

+10
-159
lines changed

Doc/library/threading.rst

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -848,9 +848,10 @@ call release as many times the lock has been acquired can lead to deadlock.
848848
reentrant lock, the same thread may acquire it again without blocking; the
849849
thread must release it once for each time it has acquired it.
850850

851-
Note that ``RLock`` is actually a factory function which returns an instance
852-
of the most efficient version of the concrete RLock class that is supported
853-
by the platform.
851+
.. versionchanged:: 3.15
852+
``RLock`` is now a class. In earlier Pythons, ``RLock`` was a factory
853+
function which returned an instance of the underlying private lock
854+
type.
854855

855856

856857
.. method:: acquire(blocking=True, timeout=-1)

Lib/test/test_free_threading/test_monitoring.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from contextlib import contextmanager
1111
from sys import monitoring
1212
from test.support import threading_helper
13-
from threading import Thread, _PyRLock, Barrier
13+
from threading import Thread, RLock, Barrier
1414
from unittest import TestCase
1515

1616

@@ -287,7 +287,7 @@ def trace(frame, event, arg):
287287

288288
sys.settrace(trace)
289289
try:
290-
l = _PyRLock()
290+
l = RLock()
291291

292292
def f():
293293
for i in range(loops):

Lib/test/test_threading.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2264,12 +2264,8 @@ def _callback_spy(self, *args, **kwargs):
22642264
class LockTests(lock_tests.LockTests):
22652265
locktype = staticmethod(threading.Lock)
22662266

2267-
class PyRLockTests(lock_tests.RLockTests):
2268-
locktype = staticmethod(threading._PyRLock)
2269-
2270-
@unittest.skipIf(threading._CRLock is None, 'RLock not implemented in C')
2271-
class CRLockTests(lock_tests.RLockTests):
2272-
locktype = staticmethod(threading._CRLock)
2267+
class RLockTests(lock_tests.RLockTests):
2268+
locktype = staticmethod(threading.RLock)
22732269

22742270
def test_signature(self): # gh-102029
22752271
with warnings.catch_warnings(record=True) as warnings_log:

Lib/threading.py

Lines changed: 1 addition & 148 deletions
Original file line numberDiff line numberDiff line change
@@ -53,10 +53,7 @@
5353
except AttributeError:
5454
_set_name = None
5555
ThreadError = _thread.error
56-
try:
57-
_CRLock = _thread.RLock
58-
except AttributeError:
59-
_CRLock = None
56+
RLock = _thread.RLock
6057
TIMEOUT_MAX = _thread.TIMEOUT_MAX
6158
del _thread
6259

@@ -123,150 +120,6 @@ def gettrace():
123120

124121
Lock = _LockType
125122

126-
def RLock():
127-
"""Factory function that returns a new reentrant lock.
128-
129-
A reentrant lock must be released by the thread that acquired it. Once a
130-
thread has acquired a reentrant lock, the same thread may acquire it again
131-
without blocking; the thread must release it once for each time it has
132-
acquired it.
133-
134-
"""
135-
if _CRLock is None:
136-
return _PyRLock()
137-
return _CRLock()
138-
139-
class _RLock:
140-
"""This class implements reentrant lock objects.
141-
142-
A reentrant lock must be released by the thread that acquired it. Once a
143-
thread has acquired a reentrant lock, the same thread may acquire it
144-
again without blocking; the thread must release it once for each time it
145-
has acquired it.
146-
147-
"""
148-
149-
def __init__(self):
150-
self._block = _allocate_lock()
151-
self._owner = None
152-
self._count = 0
153-
154-
def __repr__(self):
155-
owner = self._owner
156-
try:
157-
owner = _active[owner].name
158-
except KeyError:
159-
pass
160-
return "<%s %s.%s object owner=%r count=%d at %s>" % (
161-
"locked" if self.locked() else "unlocked",
162-
self.__class__.__module__,
163-
self.__class__.__qualname__,
164-
owner,
165-
self._count,
166-
hex(id(self))
167-
)
168-
169-
def _at_fork_reinit(self):
170-
self._block._at_fork_reinit()
171-
self._owner = None
172-
self._count = 0
173-
174-
def acquire(self, blocking=True, timeout=-1):
175-
"""Acquire a lock, blocking or non-blocking.
176-
177-
When invoked without arguments: if this thread already owns the lock,
178-
increment the recursion level by one, and return immediately. Otherwise,
179-
if another thread owns the lock, block until the lock is unlocked. Once
180-
the lock is unlocked (not owned by any thread), then grab ownership, set
181-
the recursion level to one, and return. If more than one thread is
182-
blocked waiting until the lock is unlocked, only one at a time will be
183-
able to grab ownership of the lock. There is no return value in this
184-
case.
185-
186-
When invoked with the blocking argument set to true, do the same thing
187-
as when called without arguments, and return true.
188-
189-
When invoked with the blocking argument set to false, do not block. If a
190-
call without an argument would block, return false immediately;
191-
otherwise, do the same thing as when called without arguments, and
192-
return true.
193-
194-
When invoked with the floating-point timeout argument set to a positive
195-
value, block for at most the number of seconds specified by timeout
196-
and as long as the lock cannot be acquired. Return true if the lock has
197-
been acquired, false if the timeout has elapsed.
198-
199-
"""
200-
me = get_ident()
201-
if self._owner == me:
202-
self._count += 1
203-
return 1
204-
rc = self._block.acquire(blocking, timeout)
205-
if rc:
206-
self._owner = me
207-
self._count = 1
208-
return rc
209-
210-
__enter__ = acquire
211-
212-
def release(self):
213-
"""Release a lock, decrementing the recursion level.
214-
215-
If after the decrement it is zero, reset the lock to unlocked (not owned
216-
by any thread), and if any other threads are blocked waiting for the
217-
lock to become unlocked, allow exactly one of them to proceed. If after
218-
the decrement the recursion level is still nonzero, the lock remains
219-
locked and owned by the calling thread.
220-
221-
Only call this method when the calling thread owns the lock. A
222-
RuntimeError is raised if this method is called when the lock is
223-
unlocked.
224-
225-
There is no return value.
226-
227-
"""
228-
if self._owner != get_ident():
229-
raise RuntimeError("cannot release un-acquired lock")
230-
self._count = count = self._count - 1
231-
if not count:
232-
self._owner = None
233-
self._block.release()
234-
235-
def __exit__(self, t, v, tb):
236-
self.release()
237-
238-
def locked(self):
239-
"""Return whether this object is locked."""
240-
return self._block.locked()
241-
242-
# Internal methods used by condition variables
243-
244-
def _acquire_restore(self, state):
245-
self._block.acquire()
246-
self._count, self._owner = state
247-
248-
def _release_save(self):
249-
if self._count == 0:
250-
raise RuntimeError("cannot release un-acquired lock")
251-
count = self._count
252-
self._count = 0
253-
owner = self._owner
254-
self._owner = None
255-
self._block.release()
256-
return (count, owner)
257-
258-
def _is_owned(self):
259-
return self._owner == get_ident()
260-
261-
# Internal method used for reentrancy checks
262-
263-
def _recursion_count(self):
264-
if self._owner != get_ident():
265-
return 0
266-
return self._count
267-
268-
_PyRLock = _RLock
269-
270123

271124
class Condition:
272125
"""Class that implements a condition variable.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
make threading.RLock a real class, not a factory function

0 commit comments

Comments
 (0)