Overview:
- A re-entrant lock handles the scenarios of a thread acquiring a lock multiple times. Recursion is one example of this. Recursion occurs when a function calls itself. If the Python Lock object is used in this scenario the first call to the acquire() will never have the corresponding release() called which leads to a deadlock.
- To address the limitation of not able to locking a lock object multiple times Python has implemented a lock that supports reentrancy through the class RLock.
- If a Thread locks a RLock object n times by calling acquire(), then it should also release the lock the same number of times by calling release().
- An RLock cannot be released by a thread that does not own it. In contrast to this any thread can call release() on a Lock object.
RLock internals:
- An RLock maintains a reference count along with the current owning thread of the lock object. A successful call to the acquire() method makes a thread, owner of a RLock object. The reference count of the lock object becomes one. Any subsequent calls to the acquire() method by the same thread increases the reference count by one for each call.
- When the reference count is greater than 0 no other thread can acquire the RLock object other than the thread that owns it. The reference count becomes 0 when release() is called equal to the number of times acquire() calls by the thread that owns the lock. There is no ownership of a RLock object when its reference count is 0.
- The mutual exclusion is provided by the operating system for both Lock and RLock objects. The thread ownership and the reference count is provided by the RLock class and not through the underlying OS call or underlying OS implementation.
The acquire() method of RLock class:
- The acquire() method works on blocking mode and non-blocking mode.
- Blocking mode: In blocking mode when the lock is owned by other threads, a call to acquire() blocks till the lock is available. When the lock is owned by own thread the lock is acquired immediately.
- Block till timeout: If the lock is owned by other threads the lock is acquired as soon as it is available, if that happens before the time elapses. Otherwise it blocks till timeout and returns without acquiring the lock. If the lock is owned by the current thread it is acquired immediately.
- Non-blocking mode: In non-blocking mode if the lock is available it is acquired immediately else acquire() returns without acquiring the lock.
Example:
# Example Python program that uses the RLock import threading as th # Recursive function # Create an RLock # Recursion level # Create a thread # Start the thread # Main thread should wait for the child thread to complete |
Output:
After acquire:<locked _thread.RLock object owner=6186168320 count=1 at 0x58daa387880> |