Lock a mutex multiple times in the same thread
Asked Answered
O

5

15

I'm developing an application on an embedded linux OS (uClinux) and I need to be able to lock the mutex more than once (by the same thread).

I have a mutex and a mutexattr defined and initialized as follows:

pthread_mutexattr_t waiting_barcode_mutexattr;
pthread_mutex_t waiting_barcode_mutex;

pthread_mutexattr_init(&waiting_barcode_mutexattr);
pthread_mutexattr_settype(&waiting_barcode_mutexattr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&waiting_barcode_mutex, &waiting_barcode_mutexattr);

But when I try to acquire the lock twice it blocks on the second lock:

pthread_mutex_lock(&waiting_barcode_mutex);
pthread_mutex_lock(&waiting_barcode_mutex);

Am I initializing it wrong or is there a better way of accomplishing the same?

Thanks in advance.

Conclusions:

  • Apparently PTHREAD_MUTEX_RECURSIVE or PTHREAD_MUTEX_RECURSIVE_NP don't work so I can't create a reentrant mutex.
  • try_lock is no good either. It acquires the lock if it can and returns an error if it can't acquire the lock. Unfortunately the error just informs me that the mutex is already in use and I can´t find out if the current thread already owns the lock or not.
  • pthread_mutex_lock can return an error if the current thread has the lock but for that I need to create a mutex of the type PTHREAD_MUTEX_ERRORCHECK, and I can't create one either.
Osteology answered 12/5, 2010 at 17:34 Comment(5)
Why are you trying to lock it more than once? Usually mutexes are used so that only one part of the code can have the lock at any given time.Rumba
Basically its tied to the the way I set up the code and the auxiliary functions. Maybe I should change my code and avoid this. Anyway, I'm curious.Osteology
Are you checking return value of attribute and mutex initialization calls? Might it be that your libc just doesn't support recursive locks?Unweighed
Are you trying to use semaphores ( en.wikipedia.org/wiki/Semaphore_(programming%29, minek.com/files/unix_examples/semab.html )?Commit
@Nikolai: I was forgetting to test the returns, but I tested them now and they are all returning 0. @Brian: No I'm not trying to use semaphores.Osteology
O
2

(Just realised I didn't mark this question as answered)

Taken from the Conclusions in the question:

  • Apparently PTHREAD_MUTEX_RECURSIVE or PTHREAD_MUTEX_RECURSIVE_NP don't work so I can't create a reentrant mutex.
  • try_lock is no good either. It acquires the lock if it can and returns an error if it can't acquire the lock. Unfortunately the error just informs me that the mutex is already in use and I can´t find out if the current thread already owns the lock or not.
  • pthread_mutex_lock can return an error if the current thread has the lock but for that I need to create a mutex of the type PTHREAD_MUTEX_ERRORCHECK, and I can't create one either.
Osteology answered 14/6, 2017 at 10:59 Comment(0)
M
8

Isn't this doing what you would expect?

The first call acquires the lock, and the second one will block until the first lock is released (pthread_mutex_unlock). This is what locks do.

From the documentation:

"If the mutex is already locked, the calling thread blocks until the mutex becomes available."

Perhaps you want pthread_mutex_trylock? It's hard to say unless we know what you are trying to accomplish.

CORRECTION:

I didn't see that you were setting PTHREAD_MUTEX_RECURSIVE.... Let me think about this some more.

AFTER THINKING:

From poking around google codesearch, it looks like PTHREAD_MUTEX_RECURSIVE is not implemented in all libs. You may try PTHREAD_MUTEX_RECURSIVE_NP, or you may have do something fancy to get around this.

Megmega answered 12/5, 2010 at 17:39 Comment(1)
I guess I'll have to use trylock then. I've been programming in .Net for the past two years and I was assuming that the locks were reentrant by default. Haven't programmed in C in a while.Osteology
O
3

It sounds like the pthread mutex is not reentrant. You could work around this with a flag indicating if your thread already has locked the mutex:

bool haveLock = false;// thread variable
pthread_mutex_t waiting_barcode_mutex; // also thread var

mylock()
{
   if( haveLock ) return; // no need to lock twice
   pthread_mutex_lock(&waiting_barcode_mutex);
   haveLock = true;
}

myunlock()
{
   haveLock = false;
   pthread_mutex_unlock(&waiting_barcode_mutex); // or whatever the unlock call is
}
Ous answered 12/5, 2010 at 17:40 Comment(0)
O
2

(Just realised I didn't mark this question as answered)

Taken from the Conclusions in the question:

  • Apparently PTHREAD_MUTEX_RECURSIVE or PTHREAD_MUTEX_RECURSIVE_NP don't work so I can't create a reentrant mutex.
  • try_lock is no good either. It acquires the lock if it can and returns an error if it can't acquire the lock. Unfortunately the error just informs me that the mutex is already in use and I can´t find out if the current thread already owns the lock or not.
  • pthread_mutex_lock can return an error if the current thread has the lock but for that I need to create a mutex of the type PTHREAD_MUTEX_ERRORCHECK, and I can't create one either.
Osteology answered 14/6, 2017 at 10:59 Comment(0)
L
1

Here is working code tested on UBUNTU 12.04 LTS on my Dell m6300:

  pthread_mutex_t mutex;
  pthread_mutexattr_t attr;
  int rc = pthread_mutexattr_init(&attr);
    if (rc != 0)
        throw (L"pthread_mutexattr_init returns " + rc);
    rc = pthread_mutexattr_settype (&attr, PTHREAD_MUTEX_RECURSIVE_NP);
    if (rc != 0)
        throw (L"pthread_mutexattr_settype returns " + rc);
    rc = pthread_mutex_init (&mutex, &attr);
    if (rc != 0)
        throw (L"pthread_mutex_init returns " + rc);
    rc = pthread_mutexattr_destroy(&attr);
    if (rc != 0)
        throw (L"pthread_mutexattr_destroy returns " + rc);

   //first lock
   rc = pthread_mutex_lock(&mutex);
    if (rc != 0)
        throw (L"pthread_mutex_lock returns " + rc);
   //second lock
   rc = pthread_mutex_lock(&mutex);
    if (rc != 0)
        throw (L"pthread_mutex_lock returns " + rc);

Do not forget to release the mutex as many times as you acquired it.

Luetic answered 19/6, 2013 at 19:14 Comment(0)
V
1

The code below shows that there is no problem in locking a critical section twice or thrice or N times before calling the unlock on pthread. You can do multiple locks on the same thread successively before unlocking without worrying but mind you, IT IS NOT a good programmer's practice. The right way is to call lock(), let the thread execute the critical section and call unlock(), so that other threads can execute the same piece of code between lock and unlock (called, critical section). The code below prevents any programmer's mishaps using ATTRIBUTES on pthread).

Read on!

// Example program using a thread locking multiple times sequentially before unlocking
#include <iostream>

using namespace std;

pthread_mutexattr_t     _attr;
pthread_mutex_t         _mutex;

///
/// Initialize mutex with error return locking mechanism (does not block
/// its own thread if multiple locks occurs.
///
void InitMutex()
{
   // Initialize mutex
   int ret=0;
   ret = pthread_mutexattr_settype(&_attr, PTHREAD_MUTEX_ERRORCHECK_NP);   // PTHREAD_MUTEX_ERRORCHECK_NP avoids double locking on same thread.
   if(ret != 0)
   {
      printf("Mutex attribute not initialized!!\n");
   }
   ret = pthread_mutex_init(&_mutex, &_attr);
   if(ret != 0)
   {
      printf("Mutex not initialized!!\n");
   }
}

///
/// Locks the critical section
///
int lock_me()
{
   return pthread_mutex_lock(&_mutex);
}

///
/// Unlocks the critical section
///
int unlock_me()
{
   return pthread_mutex_unlock(&_mutex);
}

int main()
{
  InitMutex(); // Very important
  int ret = 0;
  
  ret = lock_me();    // return value of 0 - OK
  cout << "First lock returns: "<< ret<< endl;
  ret = lock_me();    // returns a value like 35 - ERROR, but ignores locking again
  cout << "Second lock returns: "<< ret<< endl;
  
  // Do something in this critical section. No other thread can execute this at this time before unlock. Other threads (if any) wait at lock() waiting for main function to unlock() first.

  ret = unlock_me();  // unlocks the critical section. All is OK
  cout << "First unlock returns: "<< ret<< endl;
  ret = unlock_me();  // returns error value of 1, nothing to lock
  cout << "Second unlock returns: "<< ret<< endl;
  ret = unlock_me();  // same as above, nothing to do. Ignore and move on!
  cout << "Third unlock returns: "<< ret << endl;

  // The main() thread will never have a race condition ;) All iz well!!

  pthread_mutexattr_destroy(&_attr);    // clean up the mutex attribute
  pthread_mutex_destroy(&_mutex);       // clean up the mutex itself

}

OUTPUT:

First lock returns: 0

Second lock returns: 35

First unlock returns: 0

Second unlock returns: 1

Third unlock returns: 1

Voorhis answered 7/2, 2017 at 9:51 Comment(1)
Did you read the part of the question that states "pthread_mutex_lock can return an error if the current thread has the lock but for that I need to create a mutex of the type PTHREAD_MUTEX_ERRORCHECK, and I can't create one either."?Mineralogist

© 2022 - 2024 — McMap. All rights reserved.