User1291
User1291

Reputation: 8182

c++: mutex, guards and function calls

I was wondering. When I use a std::mutex _lock and want to rely on guards to release it, can I use it like this?

class MyClass{
private:
    mutable std::mutex _lock;
    void subroutine(){/*...*/}
public:
    void foo(){
        std::lock(_lock);std::lock_guard<std::mutex> g(_lock, std::adopt_lock);
        subroutine();
        //require I still have the lock here
        //...
        return; //g goes out of scope ==> _lock is released
    }
};

Or would the call to subroutine already cause the lock to be released? If the latter turns out to be the case, what are my options?

Update

What about this case?

class MyClass{
private:
    mutable std::mutex _lock;
public:
    void subroutine(){ //can be called on its own
        std::lock(_lock);std::lock_guard<std::mutex> g(_lock, std::adopt_lock);
        /*...*/
    }
    void foo(){
        std::lock(_lock);std::lock_guard<std::mutex> g(_lock, std::adopt_lock);
        subroutine();
        //require I still have the lock here
        //...
        return; //g goes out of scope ==> _lock is released
    }
};

Upvotes: 0

Views: 2084

Answers (1)

jensa
jensa

Reputation: 2890

The call to subroutine would not cause the lock to be released. The thread's lock on _lock is released when the std::lock_guard object goes out of scope (as you mention in your comment).

void foo(){
    std::lock_guard<std::mutex> lg{_lock}; // lock the mutex
    subroutine(); // lock is held here during this call
    // so subroutine must not attempt to lock the mutex _lock!
    return; // lg goes out of scope => its destructor is called which releases the lock on _lock
}

To answer your second question, you have a problem when foo acquires a lock on _lock and then calls subroutine, which again tries to lock _lock. A thread cannot acquire a lock on a mutex on which it already holds a lock. I would re-think your class design. If you absolutely must have this design you could do something like

void subroutine(bool called_from_foo = false)
{
    if(!called_from_foo)
        std::lock_guard<std::mutex> lg{_lock};
    /* ... */
}

void foo()
{
    std::lock_guard<std::mutex> lg{_lock};
    subroutine(true);
    /* ... */
    return;
}

Although I would not recommend this because it would be easy to use subroutine in a wrong way, causing undefined behavior in your program.

If you find yourself having to pass the owner of a lock between functions I would take a look at std::unique_lock, because this type is moveable.

Upvotes: 1

Related Questions