Question

If I understand how C++ compilers handle local variables then IsShutdownInProgress() does not need any locking since the shutdownInProgress static variable will be placed on the stack. Am I correct?

class MyClass
{
    private:
        // Irrelevant code commented away
        static pthread_mutex_t mutex;
        static bool shutdownInProgress;
    public:
        static void ShutdownIsInProgress()
        {
            pthread_mutex_lock(mutex);
            shutdownInProgress = true;                  
            pthread_mutex_unlock(mutex);
        }

        static bool IsShutdownInProgress()
        {
            // pthread_mutex_lock(mutex);
            // pthread_mutex_unlock(mutex);
            return shutdownInProgress;
        }
}
Was it helpful?

Solution

Am I correct?

No. This will make a copy of it to return; but reading it to make that copy without synchronisation will give a data race, with undefined behaviour. You'll need to make a local copy of it with the mutex locked:

static bool IsShutdownInProgress()
{
    pthread_mutex_lock(mutex);
    bool result = shutdownInProgress;
    pthread_mutex_unlock(mutex);
    return result;
}

or, using a less error-prone RAII lock type:

static bool IsShutdownInProgress()
{
    lock_guard lock(mutex);
    return shutdownInProgress;
}

In C++11, you might consider std::atomic<bool> for more convenient, and perhaps more efficient, access to simple types from multiple threads.

OTHER TIPS

Race conditions are unrelated to whether a variable is located on the heap or on the stack. A race condition is when one thread is modifying a variable (a memory location) and another thread is reading or modifying the same variable. There is no guarantee that the modification of a bool is atomic so the posted code has a race condition, and therefore undefined behaviour.

A fix would be to store the value of the bool when the mutex is held and return the variable:

static bool IsShutdownInProgress()
{
    pthread_mutex_lock(&mutex);
    bool result = shutdownInProgress;
    pthread_mutex_unlock(&mutex);
    return result;
}

c++11 introduced std::mutex and std::lock_guard that could be used and the use of the lock_guard would avoid the requirement of a temporary variable to store the bool value for return:

static std::mutex mtx_;
static bool IsShutdownInProgress()
{
    std::lock_guard<std::mutex> lk(mtx_);
    return shutdownInProgress;
}

c++11 also introduced std::atomic<> that would ensure the modification is atomic and avoid the need for an explicit lock:

static std::atomic<bool> shutdownInProgress;
static bool IsShutdownInProgress()
{
    return shutdownInProgress;
}

If c++11 is unavailable to boost::atomic was introduced in v1.53.0 and boost also has equivalent boost::mutex and boost::lock_guard.

Yes, it needs a lock

C++11's memory model states you have a data race if any threads are writing a value at the same time as another thread reading it. This is because both a read and/or a write may not be atomic.

In this case you will return a local from the function, but to get that local the compiler needs to copy the value in shutdownInProgress, which may be concurrently being changed by another thread calling ShutdownIsInProgress().

An easy way to solve this is to make shutdownInProgress an atomic:

static std::atomic<bool> shutdownInProgress;

If you make it atomic, you don't need any locks at all for either function

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top