PapaDiHatti
PapaDiHatti

Reputation: 1921

How std::mutex got unlocked in different thread?

I was reading differences between binary semaphore and mutex (Difference between binary semaphore and mutex) and one thing that i want to verify is that when a task locks (acquires) a mutex only it can unlock (release) it. If another task tries to unlock a mutex it hasn’t locked (thus doesn’t own) then an error condition is encountered and, most importantly, the mutex is not unlocked and for that i created below code in c++14:

#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
using namespace std;

int counter;
int i;
std::mutex g_pages_mutex;
void increment()
{
    std::cout<<"increment...."<<std::endl;    
    g_pages_mutex.lock();
    bool flag = g_pages_mutex.try_lock();
    std::cout<<"increment Return value is "<<flag<<std::endl;
    counter++;
    std::this_thread::sleep_for(5s);
}
void increment1()
{
    std::this_thread::sleep_for(5s);    
    std::cout<<"increment1...."<<std::endl;       
    g_pages_mutex.unlock();    
    counter++;
    bool flag = g_pages_mutex.try_lock();
    std::cout<<"increment1 Return value is "<<flag<<std::endl;
}
int main()
{
    counter = 0;
    std::thread t(increment);
    std::thread t1(increment1);
    t.join();
    t1.join();
    return 0;
}

However with this example I was able to unlock mutex from thread that doesn't own that , so just want is there some understanding gap or is this issue in c++14 std::mutex ?

Upvotes: 2

Views: 4965

Answers (3)

Tas
Tas

Reputation: 7111

From cppreference std::mutex (emphasis mine):

The behavior of a program is undefined if a mutex is destroyed while still owned by any threads, or a thread terminates while owning a mutex.

From the same site on std::mutex::try_lock and as TC points out in their answer:

If try_lock is called by a thread that already owns the mutex, the behavior is undefined.

And also std::mutex::unlock and as TC points out in their answer:

The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined.

Both your functions and threads cause undefined behaviour:

  1. increment calls lock() and then try_lock(): undefined behaviour
  2. increment1 calls unlock() before owning the mutex: undefined behaviour

Removing the try_lock() from increment will still result in undefined behaviour if you don't call unlock() before the thread ends.

You should prefer to use a std::lock_guard, or for a simple int you could also use std::atomic

Upvotes: 2

user7860670
user7860670

Reputation: 37549

The precondition for calling unlock is holding an ownership of the mutex, according to (std)30.4.1.2:

The expression m.unlock() shall be well-formed and have the following semantics:

Requires: The calling thread shall own the mutex.

Since thread executing increment1 does not hold the ownership of the mutex it detonated undefined behavior.

Upvotes: 3

T.C.
T.C.

Reputation: 137315

Calling try_lock on a std::mutex (which is not recursive) owned by the calling thread, calling unlock on a mutex not owned by the calling thread, and ending a thread while holding a mutex, all result in undefined behavior.

It might appear to succeed, it might fail and throw an exception, it might format your hard drive, it might summon nasal demons, it might time travel and correct your code for you, or it might do something else. As far as the standard is concerned, anything is permissible.

Upvotes: 10

Related Questions