1
votes

According to several documentation examples the thread can't unlock a mutex unless it locked it explicitly. Here is an excerpt from man page for pthread_mutex_unlock at IBM.

The pthread_mutex_unlock() function unlocks the mutex specified. If the calling thread does not currently hold the mutex (via a previous call to pthread_mutex_lock(), pthread_mutex_trylock(), or pthread_mutex_timedlock_np()) the unlock request fails with the EPERM error.

Even the new C++ standard says something similar about the thread ownership, yet the following program was able to unlock a mutex locked on a different thread. On gcc & Linux systems the same behavior is seen both on pthread mutex as well as std::mutex (which I believe is implemented based on pthread_mutex anyway).

#include <iostream>
#include <thread>
#include <mutex>

#include <pthread.h>

std::mutex stmutex;

pthread_mutex_t pthrmutex = PTHREAD_MUTEX_INITIALIZER;

void thread1(int i)
{
  stmutex.unlock();
  std::cout << "Un Locked in thread 1" <<  std::this_thread::get_id() << std::endl;
}

void pthread1(int i)
{
  pthread_mutex_unlock(&pthrmutex);
  std::cout << "Un Locked Pthread mutex in thread 1" <<  std::this_thread::get_id() << std::endl;
}

void thread2(int i)
{
  stmutex.lock();
  std::cout << "Locked in thread 2" <<  std::this_thread::get_id() << std::endl;
}

void thread3(int i)
{
  stmutex.unlock();
  std::cout << "UNLocked in thread 3" <<  std::this_thread::get_id() << std::endl;
}

int main(int argc, char **argv)
{
  try {
   stmutex.lock();
   std::cout << "Locked in main thread : " << std::this_thread::get_id() << std::endl;
   std::thread t1(thread1,1);
   t1.join(); 
   stmutex.lock();
   std::cout << "Locked in main thread after unlocking in thread1" << std::endl;
   stmutex.unlock();
   std::cout << "Un Locked in main thread " << std::endl;


   pthread_mutex_lock(&pthrmutex);
   std::cout << "Locked pthread mutex in main thread : " << std::this_thread::get_id() << std::endl;
   std::thread t2(pthread1,1);
   t2.join(); 
   pthread_mutex_lock(&pthrmutex);
   std::cout << "Locked Pthread mutext in main thread after unlocking in thread1" << std::endl;
   pthread_mutex_unlock(&pthrmutex);
   std::cout << "Un Locked Pthread mutext in main thread " << std::endl;

   std::thread t3(thread2,1);
   t3.join(); 
   std::thread t4(thread3,1);
   t4.join(); 

  } catch (std::exception& ex)
  {
    std::cerr << "Exception In main thread: " << ex.what() << std::endl;
  }
}

Am I missing anything in my understanding of mutex "Ownership" ?

1
It's not because it's observed to work that it's not undefined behavior. To unlock an std::mutex without owning the lock is undefined behavior. As for the pthread part of the question, you never check any return codes. - François Andrieux
Is there a reason you are still using p_thread rather than the new Standard Library stuff? - Jive Dadson
@JiveDadson The example uses both standard and pthread. - dors
Why not just standard? Can the two be mixed legally? I don't know. - Jive Dadson
std::mutex::unlock(): "The mutex must be locked by the current thread of execution, otherwise, the behavior is undefined." source: en.cppreference.com/w/cpp/thread/mutex/unlock Reasoning about UB is a waste of time. - Richard Critten

1 Answers

0
votes

The request fails with the EPERM error only for mutexes created with PTHREAD_MUTEX_ERRORCHECK.

See pthread_mutex_lock section RATIONALE:

... while being able to extract the thread ID of the owner of a mutex might be desirable, it would require storing the current thread ID when each mutex is locked, and this could incur unacceptable levels of overhead.

I.e. initialize your mutex with PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP.

Example:

void locker(pthread_mutex_t* mutex) {
    if(int e = pthread_mutex_lock(mutex))
        fprintf(stderr, "pthread_mutex_lock: (%d)%s\n", e, strerror(e));
}

void unlocker(pthread_mutex_t* mutex) {
    if(int e = pthread_mutex_unlock(mutex))
        fprintf(stderr, "pthread_mutex_unlock: (%d)%s\n", e, strerror(e));
}

int main() {
    pthread_mutex_t a = PTHREAD_MUTEX_INITIALIZER;
    pthread_mutex_t b = PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
    std::thread(locker, &a).join();
    std::thread(locker, &b).join();
    std::thread(unlocker, &a).join();
    std::thread(unlocker, &b).join(); // pthread_mutex_unlock: (1)Operation not permitted
}