Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Locking a mutex in a destructor in C++11

I have some code which need to be thread safe and exception safe. The code below is a very simplified version of my problem :

#include <mutex>
#include <thread>

std::mutex mutex;
int n=0;

class Counter{
public:
    Counter(){
        std::lock_guard<std::mutex>guard(mutex);
        n++;}
    ~Counter(){
        std::lock_guard<std::mutex>guard(mutex);//How can I protect here the underlying code to mutex.lock() ?
        n--;}
};

void doSomething(){
    Counter counter;
    //Here I could do something meaningful
}

int numberOfThreadInDoSomething(){
    std::lock_guard<std::mutex>guard(mutex);
    return n;}

I have a mutex that I need to lock in the destructor of an object. The problem is that my destructor should not throw exceptions.

What can I do ?

0) I cannot replace n with an atomic variable (of course it would do the trick here but that is not the point of my question)

1) I could replace my mutex with a spin lock

2) I could try and catch the locking into an infinite loop until I eventualy acquire the lock with no exception raised

None of those solution seems very appealing. Did you have the same problem ? How did you solve it ?

like image 655
Arnaud Avatar asked May 15 '13 15:05

Arnaud


People also ask

How do you lock a mutex?

Use pthread_mutex_lock(3THR) to lock the mutex pointed to by mutex . When pthread_mutex_lock() returns, the mutex is locked and the calling thread is the owner. If the mutex is already locked and owned by another thread, the calling thread blocks until the mutex becomes available.

What is mutex lock in C++?

The mutex class is a synchronization primitive that can be used to protect shared data from being simultaneously accessed by multiple threads. mutex offers exclusive, non-recursive ownership semantics: A calling thread owns a mutex from the time that it successfully calls either lock or try_lock until it calls unlock .

How do you lock and unlock a mutex?

A recursive mutex can be locked repeatedly by the owner. The mutex does not become unlocked until the owner has called pthread_mutex_unlock() for each successful lock request that it has outstanding on the mutex. An errorcheck mutex checks for deadlock conditions that occur when a thread relocks an already held mutex.

What happens when mutex lock?

Mutexes are used to protect shared resources. If the mutex is already locked by another thread, the thread waits for the mutex to become available. The thread that has locked a mutex becomes its current owner and remains the owner until the same thread has unlocked it.


1 Answers

As suggested by Adam H. Peterson, I finally decided to write a no throw mutex :

class NoThrowMutex{
private:
    std::mutex mutex;
    std::atomic_flag flag;
    bool both;
public:
    NoThrowMutex();
    ~NoThrowMutex();
    void lock();
    void unlock();
};

NoThrowMutex::NoThrowMutex():mutex(),flag(),both(false){
    flag.clear(std::memory_order_release);}

NoThrowMutex::~NoThrowMutex(){}

void NoThrowMutex::lock(){
    try{
        mutex.lock();
        while(flag.test_and_set(std::memory_order_acquire));
        both=true;}
    catch(...){
        while(flag.test_and_set(std::memory_order_acquire));
        both=false;}}

void NoThrowMutex::unlock(){
    if(both){mutex.unlock();}
    flag.clear(std::memory_order_release);}

The idea is to have two mutex instead of only one. The real mutex is the spin mutex implemented with an std::atomic_flag. This spin mutex is protected by a std::mutex which could throw.

In a normal situation, the standard mutex is acquired and the flag is set with a cost of only one atomic operation. If the standard mutex cannot be locked immediately, the thread is going to sleep.

If for any reason the standard mutex throws, the mutex will enter its spin mode. The thread where the exception occured will then loop until it can set the flag. As no other thread is aware that this thread bybassed completely the standard mutex, they could spin too.

In the worst case scenario, this locking mechanism degrades to a spin lock. Most of the time it reacts just like a normal mutex.

like image 115
Arnaud Avatar answered Oct 11 '22 19:10

Arnaud