views:

358

answers:

4

Hi guys. I am running some thread safe code here. I am using a mutex to protect the section of code that needs to be run only by only 1 thread at a time. The problem I have is using this code sometimes I end up with 2 Mutex objects. This is a static function by the way. How do I make sure only 1 mutex object gets created??

/*static*/ MyClass::GetResource()
{

if (m_mutex == 0)
{
// make a new mutex object
m_mutex = new MyMutex();
}

m_mutex->Lock();
+7  A: 

The issue is the thread could be interrupted after checking if m_mutex is 0, but not before it creates the mutex, allowing another thread to run through the same code.

Don't assign to m_mutex right away. Create a new mutex, and then do an atomic compare exchange.

You don't mention your target platform, but on Windows:

MyClass::GetResource()
{
    if (m_mutex == 0)
    {
        // make a new mutex object
        MyMutex* mutex = new MyMutex();

        // Only set if mutex is still NULL.
        if (InterlockedCompareExchangePointer(&m_mutex, mutex, 0) != 0)
        {
           // someone else beat us to it.
           delete mutex;
        }
    }
    m_mutex->Lock();

Otherwise, replace with whatever compare/swap function your platform provides.

Another option is to use one-time initialization support, which is available on Windows Vista and up, or just pre-create the mutex if you can.

Michael
example please?
shergill
Your current example has a memory leak - you need to ensure to delete the mutex instance that lost the race
1800 INFORMATION
@1800 - Thanks for pointing that out, it'd be awful if someone copied the code as is. Fixed.
Michael
+10  A: 

Simply create m_mutex outside of GetResource(), before it can ever be called - this removes the critical section around the actual creation of the mutex.

MyClass::Init()
{
  m_mutex = new Mutex;
}    

MyClass::GetResource()
{
  m_mutex->Lock();
  ...
  m_mutex->Unlock();
}
Justicle
+1. Trying to do lazy mutex creation will lead to a world of hurt.
Logan Capaldo
MyClass::GetResource is a static function :(
shergill
@shergill So is MyClass::Init, in this example. The point is to make sure Init() is called before any calls to GetResource.
Andres Jaan Tack
@shergill - unless `GetResource()` is being called before main(), in which case you have much bigger issues, it should be easy to find a place for `Init()` to be called.
Justicle
+2  A: 

Why use a pointer anyway? Why not replace the pointer with an actual instance that does not require dynamic memory management? This avoids the race condition, and does not impose a performance hit on every call into the function.

1800 INFORMATION
+3  A: 

Lazy mutex initialization isn't really appropriate for static methods; you need some guarantee that nobody races to the initialization. The following uses the compiler to generate a single static mutex for the class.

/* Header (.hxx) */
class MyClass
{
    ...

  private:
    static mutable MyMutex m_mutex;  // Declares, "this mutex exists, somewhere."
};


/* Compilation Unit (.cxx) */
MyMutex MyClass::m_mutex;            // The aforementioned, "somewhere."

MyClass::GetResource()
{
    m_mutex.Lock();
    ...
    m_mutex.Unlock();
}

Some other solutions will require extra assumptions of your fellow programmers. With the "call init()" method, for instance, you have to be sure that the initialization method were called, and everybody would have to know this rule.

Andres Jaan Tack