Reentrant mutex
Page Module:Message box/ambox.css has no content.Page Template:Multiple issues/styles.css has no content.
This article has multiple issues. Please help improve it or discuss these issues on the talk page. (Learn how and when to remove these messages)
Page Module:Message box/ambox.css has no content.
|
In computer science, the reentrant mutex (also known as a recursive mutex or recursive lock) is a synchronization primitive that may be locked multiple times by the same thread without causing a deadlock.
While a thread that attempts to lock a standard (non-reentrant) mutex that it already holds would block indefinitely, this operation succeeds on a reentrant mutex. This is achieved by associating the mutex with the thread that owns it and maintaining a lock count. The owning thread can acquire the lock multiple times, incrementing the count each time. The lock is only released for other threads to acquire once the owning thread has unlocked it the same number of times it was acquired, bringing the count to zero.
Motivation
A reentrant mutex solves deadlocks that can occur when a function needs to acquire a lock that is already held by the same thread. This often happens in recursive code or when one function that acquires a lock calls another function that must acquire the same lock. [1]
Recursive mutexes solve the problem of non-reentrancy with regular mutexes: if a function that takes a lock and executes a callback is itself called by the callback, deadlock ensues.[2] In pseudocode, that is the following situation:
Consider the following scenario in pseudocode:
var m : Mutex // A standard, non-reentrant mutex, initially unlocked.
function lock_and_call(i : Integer)
m.lock()
callback(i)
m.unlock()
function callback(i : Integer)
if i > 0
lock_and_call(i - 1)
lock_and_call(1) // Invoking the function
When Page Template:Mono/styles.css has no content.lock_and_call(1) is executed with a standard mutex, it results in a deadlock:
- The initial call to Page Template:Mono/styles.css has no content.lock_and_call(1) successfully acquires the lock Page Template:Mono/styles.css has no content.m.
- It then calls Page Template:Mono/styles.css has no content.callback(1).
- Inside Page Template:Mono/styles.css has no content.callback(1), because Page Template:Mono/styles.css has no content.i > 0, it calls Page Template:Mono/styles.css has no content.lock_and_call(0).
- This second call to Page Template:Mono/styles.css has no content.lock_and_call attempts to acquire the lock Page Template:Mono/styles.css has no content.m again.
- Deadlock: Because the mutex Page Template:Mono/styles.css has no content.m is already locked, the thread stops and waits for the lock to be released. However, it is the thread itself that holds the lock, so it is waiting for itself to complete an action it can never take.
Using a reentrant mutex for Page Template:Mono/styles.css has no content.m prevents this deadlock. When the second call to Page Template:Mono/styles.css has no content.lock_and_call(0) attempts to lock the mutex, the operation succeeds because the thread attempting to acquire the lock is already the owner. The mutex's internal count is incremented. The lock is only fully released when both calls to Page Template:Mono/styles.css has no content.lock_and_call have completed and performed their corresponding Page Template:Mono/styles.css has no content.m.unlock() operations.
Practical use
W. Richard Stevens notes that recursive locks are "tricky" to use correctly, and recommends their use for adapting single-threaded code without changing APIs, but "only when no other solution is possible".[3]
The Java language's native synchronization mechanism, monitor, uses recursive locks. Syntactically, a lock is a block of code with the 'synchronized' keyword preceding it and any Object reference in parentheses that will be used as the mutex. Inside the synchronized block, the given object can be used as a condition variable by doing a wait(), notify(), or notifyAll() on it. Thus all Objects are both recursive mutexes and condition variables.[4]
Example
- Thread A calls function F which acquires a reentrant lock for itself before proceeding
- Thread B calls function F which attempts to acquire a reentrant lock for itself but cannot due to one already outstanding, resulting in either a block (it waits), or a timeout if requested
- Thread A's F calls itself recursively. It already owns the lock, so it will not block itself (no deadlock). This is the central idea of a reentrant mutex, and is what makes it different from a regular lock.
- Thread B's F is still waiting, or has caught the timeout and worked around it
- Thread A's F finishes and releases its lock(s)
- Thread B's F can now acquire a reentrant lock and proceed if it was still waiting
Software emulation
Software emulation can be accomplished[<span title="Script error: No such module "decodeEncode".">clarification needed] using the following structure:[citation needed]
- A "control" condition using a regular lock
- Owner identifier, unique to each thread (defaulting to empty / not set)
- Acquisition count (defaulting to zero)
Acquisition
- Acquire the control condition.
- If the owner is set and not the current thread, wait for the control condition to be notified (this also releases the condition).
- Set the owner to the current thread. The owner identifier should have already been cleared at this point unless the acquirer is already the owner.
- Increment the acquisition count (should always result in 1 for new owners).
- Release the control condition.
Release
- Acquire the control condition, asserting that the owner is the releaser.
- Decrement the acquisition count, asserting that the count is greater than or equal to zero.
- If the acquisition count is zero, clear the owner information and notify the control condition.
- Release the control condition.
References
Page Template:Reflist/styles.css has no content.
- ^ Page Module:Citation/CS1/styles.css has no content.Buschmann, Frank; Henney, Kevlin; Schmidt, Douglas C. (2007). Pattern-Oriented Software Architecture, A Pattern Language for Distributed Computing. John Wiley & Sons. p. 374. ISBN 9780470065303.
- ^ Page Module:Citation/CS1/styles.css has no content.Buschmann, Frank; Henney, Kevlin; Schmidt, Douglas C. (2007). Pattern-Oriented Software Architecture, A Pattern Language for Distributed Computing. John Wiley & Sons. p. 374. ISBN 9780470065303.
- ^ Page Module:Citation/CS1/styles.css has no content.Stevens, W. Richard; Rago, Stephen A. (2013). Advanced Programming in the UNIX Environment. Addison-Wesley. p. 434.
- ^ Page Module:Citation/CS1/styles.css has no content.David Hovemeyer. "Lecture 17: Java Threads, Synchronization". CS 365 - Parallel and Distributed Computing. Archived from the original on 16 February 2015. Retrieved 4 June 2015.
{{cite book}}:|work=ignored (help)