Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recursive and Non-Recursive Locks (Mutex)

I am having problems with deadlocking in my program. So I have been reading about the locks, but the problem is most information is inconsistent or not platform defined. At the Recursive Lock (Mutex) vs Non-Recursive Lock (Mutex) the most accepted answer says:

Because the recursive mutex has a sense of ownership, the thread that grabs the mutex must be the same thread that release the mutex. In the case of non-recursive mutexes, there is no sense of ownership and any thread can usually release the mutex no matter which thread originally took the mutex. In many cases, this type of "mutex" is really more of a semaphore action, where you are not necessarily using the mutex as an exclusion device but use it as synchronization or signaling device between two or more threads.

In the commentaries people say that is not correct and there is no reference about it. So...

1) If I lock a non-recursive mutex in thread A. Can thread B unlock it without grabbing the lock?

2) If in a lock was taken in a Non-Recursive mutex by thread A and thread B calls to get the lock, will thread B wait until the lock has been released to get the lock or will it throw an exception? What about this case in a recursive mutex? (Also discussed in other questions where no decent conclusion could be made)

3) When using recursive locks, on process termination, do all my recursive locks have to be released? (Depending where the process ends that does not happen)

4) What issues am I looking at when using a combination of recursive and non-recursive locks with caution?

PS: Using only windows platform and std::thread.

like image 940
Rodrigo Rutsatz Avatar asked Dec 25 '22 02:12

Rodrigo Rutsatz


2 Answers

I think you'll be helped immensely by reading the wiki on Reentrant Mutexes. I would agree with the comments in that other thread; The accepted answer is wrong, or at least explains its point very, very poorly.

All Mutexes have a notion of ownership. That's what makes them different from a Semaphore. The thread that locks a mutex is always the thread that has to unlock it, and that's part of why mutexes can cause deadlock, but also why they work for their intended purpose (to mutually exclude access to a particular block of code).

So what is the difference between a Recursive/Reenrant and regular Mutex? A Recursive mutex can be locked multiple times by the same thread. To quote the wiki:

Recursive locks (also called recursive thread mutex) are those that allow a thread to recursively acquire the same lock that it is holding. Note that this behavior is different from a normal lock. In the normal case if a thread that is already holding a normal lock attempts to acquire the same lock again, then it will deadlock.

This is the entirety of the difference between the two mutex types. Essentially, you need a recursive mutex if you're placing a mutex lock inside a recursive method and the method recurses before the mutex is freed. Otherwise after the first recursion, there will be instant deadlock because the lock cannot be acquired a second time.

Really, this is the only reason to use a recursive mutex; Most other situations where you'd get the same thread trying to acquire the same lock without freeing it can probably be refactored into properly acquiring/freeing the lock without the need for a recursive mutex. And doing so will be much safer; A recursive function is naturally going to bubble out and free every lock on the recursive mutex assuming RAII, where as in other situations you may not sufficiently free the mutex and still wind up with deadlock.

So, to answer your specific questions:

  1. No, unless your mutex system specifically allows this
  2. Yes in both cases generally, though again this is mutex implementation specific with regards to blocking/throwing. Almost every system I've ever used just blocks (and that's why non-recursive mutexes deadlock if the same thread locks twice without a free)
  3. Yes, usually, though generally they will be freed assuming proper RAII and the process terminates gracefully. Processes terminating non-gracefully while holding locks can be a bit of a crapshoot.
  4. See my above explanations. Specifically, draw attention to the following from the wiki:

Note that the recursive lock is said to be released if and only if the number of times it has been acquired matches the number of times it has been released by the owner thread.

like image 173
aruisdante Avatar answered Jan 02 '23 02:01

aruisdante


You are referring to POSIX mutexes discussion but Windows does not support POSIX anyway and you use c++ standard threading primitives that may differ in details.

So better check standard library documentation first, for instance c++ standard for unlock explicitly states:

Requires: The calling thread shall own the mutex.

like image 41
dewaffled Avatar answered Jan 02 '23 00:01

dewaffled