c++ – Thread-safe object creation of local static mutex?


Suppose you want to organize the protection of arbitrary code with a local static mutex:

void MyClass::myMethod() {
    static QMutex mutex;

    // защищённый код ...

As far as I know, if instead QMutex there is another local static type (for example, QString ), then a "race" can easily happen and, say, two competing threads will create two copies of the object.

Although the Qt help for QMutex states that all methods are thread-safe, it is not certain that the same is true of its constructor.

In the context of MyClass::myMethod() will the creation of the mutex object on the first call be thread-safe?


Since C++11, static variable initialization is thread-safe ( §6.7 ):

otherwise such a variable is initialized the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization .

For earlier standards, this was not guaranteed, although some compilers provided such protection (for example, GCC).

Consider the QMutex constructor:

QMutex::QMutex(RecursionMode mode)
    d_ptr.store(mode == Recursive ? new QRecursiveMutexPrivate : 0);

Qt makes extensive use of the Pimpl idiom, d_ptr is just an implementation pointer and is of type QBasicAtomicPointer<QMutexData> . When accessing a constructor multithreaded, there may be cases where the QRecursiveMutexPrivate object will be constructed multiple times, resulting in memory leaks. If the mutex is non-recursive, then the constructor is thread-safe.

Another, more serious, problem is related to the race condition : for example, two threads A and B start initializing a mutex variable, thread A completes the initialization and calls the lock() function, control is transferred to thread B , which in turn completes the initialization, which results in to undefined behavior.

Scroll to Top