1

I am reading the book "C++ Concurrency In Action" and have some question about the mutex used in listing 6.1, the code snippet is below:

void pop(T& value)
{
    std::lock_guard<std::mutex> lock(m);
    if(data.empty()) throw empty_stack();
    value=std::move(data.top());
    data.pop();
}
bool empty() const
{
    std::lock_guard<std::mutex> lock(m);
    return data.empty();
}

The pop method locks the mutex and then calls the empty mutex. But the mutex is not a recursive_mutex, and the code works properly. So I doubt what is the actually difference between std::mutex and std::recursive_mutex.

jotik
  • 17,044
  • 13
  • 58
  • 123
vmcloud
  • 650
  • 4
  • 13

2 Answers2

4

It is calling data.empty() which seems like a function from a data member. Not the same as the empty function you show.

If it were, this would be a recursive call

bool empty() const
{
    std::lock_guard<std::mutex> lock(m);
    return data.empty();
}

and nothing would work.

Bo Persson
  • 90,663
  • 31
  • 146
  • 203
2

well, recursive_mutex is for... recursive function!

In some Operating systems, locking the same mutex twice can lead to a system error( in which, the lock may be released copmletely, application may crash and actually all kind of weird and undefined behaviour may occur).

look at this (silly example)

void recursivePusher(int x){
   if (x>10){
     return;
   }

   std::lock_guard<std::mutex> lock(m);
   queue.push(x);
   recursivePusher(x+1);

}

this function recursivly increments x and pushes it into some shared queue. as we talked above - the same lock may not be locked twice by the same thread, but we do need to make sure the shared queue isn't baing altered by mutilple threads.

one easy solution is to move the lociking outside the recursive function, but what happens if we can't do it? what happens if the function called is the only one that can lock the shared resource?

for example, my calling function may look like this:

switch(option){

case case1: recursivly_manipulate_shared_array(); break;
case case2: recursivly_manipulate_shared_queue(); break;
case case3: recursivly_manipulate_shared_map(); break;

}

ofcourse, you wouldn't lock all three(shred_Array,shared_map,shared_queue) only for one of them will be altered.

the solution is to use std::shared_mutex :

void recursivePusher(int x){
   if (x>10){
     return;
   }

   std::lock_guard<std::recursive_mutex> lock(m);
   queue.push(x);
   recursivePusher(x+1);

}

if the same thread don't need to lock the mutex recursivly it should use regular std::mutex, like in your example.

PS. in your snippet, empty is not the same as T::empty. calling data.empty() doesn't call empty recursivley.

David Haim
  • 25,446
  • 3
  • 44
  • 78
  • `recursive_mutex` is not just for recursive functions. Consider a function calling another which protects the same resources as the first one. – Caglayan DOKME Mar 25 '23 at 14:16