Nerdy
Nerdy

Reputation: 1089

Ensuring mutex lock is called before unlock

Was trying to execute a piece of code only when mutex is locked. Methods in class mainclass does the task only when the mutex is locked. To avoid creating undefined behaviour when the mutex is unlocked without locking ( considering case where locking may fail), lock and unlock is handled with constructor and destructor of mutexclass.

I want to check if mutex is locked before executing the code from mainclass methods calling1() and calling2(). As constructor won't return values, want to know alternate methods of doing this.

#include <iostream>
using namespace std;

class mutexclass
{
    public:
    pthread_mutex_t *mutex;
    bool a = true;
    mutexclass(pthread_mutex_t* inmutex)
    {   mutex= inmutex;
        int err=pthread_mutex_lock(mutex);
        cout<<"automutex constructor";
        if(err != 0)
        a = false;
    };
    ~mutexclass()
    {
        if(a)
        {
            pthread_mutex_unlock(mutex);
            cout<<"automutex destructor";
        }
        else
            cout<<"a is not true";
    };
};  
class mainclass{
    public:
    mainclass();
    ~mainclass();
    pthread_mutex_t lock;
    void calling1();
    void calling2();
};
mainclass::mainclass()
{
    pthread_mutex_init(&lock,NULL);
}
mainclass::~mainclass()
{
    pthread_mutex_destroy(&lock);
}
void mainclass::calling1()
{
    mutexclass m = mutexclass(&lock);
    //call cout only if mutex lock is successful
    cout<<"calling1";
}
void mainclass::calling2()
{
    mutexclass m = mutexclass(&lock);
    cout<<"calling2";
}

int main() {
    mainclass s;

    s.calling1();
    s.calling2();
    return 0;
}

Upvotes: 1

Views: 330

Answers (2)

Nikos C.
Nikos C.

Reputation: 51832

You should really use std::mutex and std::lock_guard, and in general the standard C++ threading support. But if for some reason you can't (maybe this is a learning exercise?) then I recommend preventing mutexclass from ever being in a state where the mutex is not locked. That should be an error, so throw an exception if the lock can't be acquired.

It's also important to prevent instances from being copied. Otherwise, the same mutex will be unlocked multiple times in the destructor of each copy. To prevent that, delete the copy constructor and assignment operator.

#include <system_error>

class mutexclass
{
public:
    explicit mutexclass(pthread_mutex_t* inmutex)
        : mutex(inmutex)
    {
        int err = pthread_mutex_lock(mutex);
        if (err != 0) {
            throw std::system_error(err, std::generic_category(),
                "pthread mutex lock acquisition failure");
        }
    }

    ~mutexclass()
    {
        pthread_mutex_unlock(mutex);
    }

    mutexclass(const mutexclass&) = delete;
    mutexclass& operator=(const mutexclass&) = delete;

private:
    pthread_mutex_t* mutex;
};

Now you can implement the calling* functions like this:

void mainclass::calling1()
{
    try {
        mutexclass m(&lock);
        cout << "calling1\n";
    } catch (const std::exception& e) {
        // Could not lock.
    }
}

Upvotes: 4

VLL
VLL

Reputation: 10155

You set a boolean to indicate whether the mutex is locked or not. You can also check that value outside the class:

class mutexclass
{
public:
    mutexclass(pthread_mutex_t* inmutex)
    {   
        mutex = inmutex;
        int err = pthread_mutex_lock(mutex);
        if(err == 0) locked_ = true;
    }
    ~mutexclass()
    {
        if(locked_) {
            pthread_mutex_unlock(mutex);
        }
    }
    bool locked() const { return locked_; }

private:
    pthread_mutex_t *mutex;
    bool locked_ = false;
};  

void mainclass::calling1()
{
    mutexclass m = mutexclass(&lock);
    //call cout only if mutex lock is successful
    if (m.locked()) {
        cout<<"calling1";
    }
}

You could also use std::mutex, which throws an exception on errors.

Upvotes: 1

Related Questions