sflee
sflee

Reputation: 1719

Is it safe to implement Double Check Lock Pattern in C++ before C++11

class SelfTesting
{
private:
    char * pChar;
    SelfTesting()
    {
        pChar = new char[1024 * 1024 * 1024];
    }
public:
    static SelfTesting * pSelf;

    static SelfTesting& GetInst()
    {
        if (!pSelf)
        {
            boost::lock_guard<boost::mutex> lock(g_boost_mutex);
            if (!pSelf)
            {
                pSelf = new SelfTesting;
            }
        }
        return *pSelf;
    }
};

Generally, I know the problem is caused by:

1. allocate memory for `SelfTesting`
2. store the pointer to the allocated memory in `pChar`
3. initialize `SelfTesting`
  1. If other thread touch the pointer between step 2 and 3, data race condition happens. From
  2. If the pointer copying is not atomic, data race condition can also happens. From

I know that I can use local static variable to implement this pattern in C++11. My question is that is the above implementation thread safe or it is undefined when I am using C++ standard before C++11. Is boost::mutex make sure that pSelf will update after lock is died?

Upvotes: 4

Views: 248

Answers (1)

Sebastian Redl
Sebastian Redl

Reputation: 72044

As written, this pattern is not safe in any version of C++. In C++11 terms, you have a data race between the outer read of pSelf and the write of pSelf.

In general, pre-C++11, there's no such thing as guaranteed safety of multi-threaded code. The most important thing about C++11 was that it introduced into the abstract model of C++ execution the idea that there might be multiple threads of execution in the first place. Before that, there were absolutely no guarantees whatsoever about multi-threaded execution, because the concept didn't exist. Any situation where you have more than one thread is undefined as far as the standard is concerned, because it doesn't define what a thread is.

This means basically that any multi-threaded code you write in C++98 is entirely dependent on the particular implementation you use. There were some things you could rely on with mainstream compilers, though in the development of C++11, several behaviors (especially optimizations) were found in the various compilers that were not actually safe when there were multiple threads.

But this is all irrelevant, because the code as you wrote it was never guaranteed to be safe in any compiler I'm aware of. With Visual C++, you could have made it safe by making pSelf volatile (VC++ treated volatile variables sort of like atomics), but that wouldn't have worked in GCC. In GCC, you would have to use the atomic intrinsics, or the Boost.Atomic library.

Upvotes: 3

Related Questions