In computer science, the reentrant mutex is a particular type of mutual exclusion device that may be locked multiple times by the same process/thread, without causing a deadlock. While any attempt to perform the "lock" operation on an ordinary mutex would either fail or block when the mutex is already locked, on a recursive mutex this operation will succeed if and only if the locking thread is the one that already holds the lock. Typically, a recursive mutex tracks the number of times it has been locked, and requires equally many unlock operations to be performed before other threads may lock it.
Motivation
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. In pseudocode, that is the following situation: var m : Mutex // A non-recursive mutex, initially unlocked. function lock_and_call m.lock callback m.unlock function callback if i > 0 lock_and_call lock_and_call // Invoking the function Given these definitions, the function call will cause the following sequence of events:
— mutex locked
— because
— deadlock, because is already locked, so the executing thread will block, waiting for itself.
Replacing the mutex with a recursive one solves the problem, because the final will succeed without blocking.
Practical use
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". 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.
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, or a timeout if requested
Thread A's F calls itself recursively. It already owns the lock, so it will not block itself. 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
Thread B's F can now acquire a reentrant lock and proceed if it was still waiting