[ACCEPTED]-Thread safe lazy construction of a singleton in C++-lazy-initialization

Accepted answer
Score: 14

Unfortunately, Matt's answer features what's 46 called double-checked locking which isn't supported by the C/C++ memory 45 model. (It is supported by the Java 1.5 44 and later — and I think .NET — memory model.) This 43 means that between the time when the pObj == NULL check 42 takes place and when the lock (mutex) is 41 acquired, pObj may have already been assigned 40 on another thread. Thread switching happens 39 whenever the OS wants it to, not between 38 "lines" of a program (which have no meaning 37 post-compilation in most languages).

Furthermore, as 36 Matt acknowledges, he uses an int as a lock 35 rather than an OS primitive. Don't do that. Proper 34 locks require the use of memory barrier 33 instructions, potentially cache-line flushes, and 32 so on; use your operating system's primitives 31 for locking. This is especially important 30 because the primitives used can change between 29 the individual CPU lines that your operating 28 system runs on; what works on a CPU Foo 27 might not work on CPU Foo2. Most operating 26 systems either natively support POSIX threads 25 (pthreads) or offer them as a wrapper for 24 the OS threading package, so it's often 23 best to illustrate examples using them.

If 22 your operating system offers appropriate 21 primitives, and if you absolutely need it 20 for performance, instead of doing this type 19 of locking/initialization you can use an 18 atomic compare and swap operation to initialize a shared global 17 variable. Essentially, what you write will 16 look like this:

MySingleton *MySingleton::GetSingleton() {
    if (pObj == NULL) {
        // create a temporary instance of the singleton
        MySingleton *temp = new MySingleton();
        if (OSAtomicCompareAndSwapPtrBarrier(NULL, temp, &pObj) == false) {
            // if the swap didn't take place, delete the temporary instance
            delete temp;
        }
    }

    return pObj;
}

This only works if it's safe 15 to create multiple instances of your singleton 14 (one per thread that happens to invoke GetSingleton() simultaneously), and 13 then throw extras away. The OSAtomicCompareAndSwapPtrBarrier function provided 12 on Mac OS X — most operating systems provide 11 a similar primitive — checks whether pObj is 10 NULL and only actually sets it to temp to it if 9 it is. This uses hardware support to really, literally 8 only perform the swap once and tell whether 7 it happened.

Another facility to leverage 6 if your OS offers it that's in between these 5 two extremes is pthread_once. This lets you set up 4 a function that's run only once - basically 3 by doing all of the locking/barrier/etc. trickery 2 for you - no matter how many times it's 1 invoked or on how many threads it's invoked.

Score: 14

Basically, you're asking for synchronized 45 creation of a singleton, without using any 44 synchronization (previously-constructed 43 variables). In general, no, this is not 42 possible. You need something available 41 for synchronization.

As for your other question, yes, static 40 variables which can be statically initialized 39 (i.e. no runtime code necessary) are guaranteed 38 to be initialized before other code is executed. This 37 makes it possible to use a statically-initialized 36 mutex to synchronize creation of the singleton.

From 35 the 2003 revision of the C++ standard:

Objects 34 with static storage duration (3.7.1) shall 33 be zero-initialized (8.5) before any other 32 initialization takes place. Zero-initialization 31 and initialization with a constant expression 30 are collectively called static initialization; all 29 other initialization is dynamic initialization. Objects 28 of POD types (3.9) with static storage duration 27 initialized with constant expressions (5.19) shall 26 be initialized before any dynamic initialization 25 takes place. Objects with static storage 24 duration defined in namespace scope in the 23 same translation unit and dynamically initialized 22 shall be initialized in the order in which 21 their definition appears in the translation 20 unit.

If you know that you will be using this 19 singleton during the initialization of other 18 static objects, I think you'll find that 17 synchronization is a non-issue. To the 16 best of my knowledge, all major compilers 15 initialize static objects in a single thread, so 14 thread-safety during static initialization. You 13 can declare your singleton pointer to be 12 NULL, and then check to see if it's been 11 initialized before you use it.

However, this 10 assumes that you know that you'll use this singleton 9 during static initialization. This is also 8 not guaranteed by the standard, so if you 7 want to be completely safe, use a statically-initialized 6 mutex.

Edit: Chris's suggestion to use an 5 atomic compare-and-swap would certainly 4 work. If portability is not an issue (and 3 creating additional temporary singletons 2 is not a problem), then it is a slightly 1 lower overhead solution.

Score: 12

Here's a very simple lazily constructed 9 singleton getter:

Singleton *Singleton::self() {
    static Singleton instance;
    return &instance;
}

This is lazy, and the next 8 C++ standard (C++0x) requires it to be thread 7 safe. In fact, I believe that at least g++ implements 6 this in a thread safe manner. So if that's 5 your target compiler or if you use a compiler 4 which also implements this in a thread safe 3 manner (maybe newer Visual Studio compilers 2 do? I don't know), then this might be all 1 you need.

Also see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2513.html on this topic.

Score: 8

You can't do it without any static variables, however 5 if you are willing to tolerate one, you 4 can use Boost.Thread for this purpose. Read the "one-time 3 initialisation" section for more info.

Then 2 in your singleton accessor function, use 1 boost::call_once to construct the object, and return it.

Score: 6

For gcc, this is rather easy:

LazyType* GetMyLazyGlobal() {
    static const LazyType* instance = new LazyType();
    return instance;
}

GCC will make 16 sure that the initialization is atomic. For VC++, this is not the case. :-(

One 15 major issue with this mechanism is the lack 14 of testability: if you need to reset the 13 LazyType to a new one between tests, or 12 want to change the LazyType* to a MockLazyType*, you 11 won't be able to. Given this, it's usually 10 best to use a static mutex + static pointer.

Also, possibly 9 an aside: It's best to always avoid static 8 non-POD types. (Pointers to PODs are OK.) The 7 reasons for this are many: as you mention, initialization 6 order isn't defined -- neither is the order 5 in which destructors are called though. Because 4 of this, programs will end up crashing when 3 they try to exit; often not a big deal, but 2 sometimes a showstopper when the profiler 1 you are trying to use requires a clean exit.

Score: 1

While this question has already been answered, I 5 think there are some other points to mention:

  • If you want lazy-instantiation of the singleton while using a pointer to a dynamically allocated instance, you'll have to make sure you clean it up at the right point.
  • You could use Matt's solution, but you'd need to use a proper mutex/critical section for locking, and by checking "pObj == NULL" both before and after the lock. Of course, pObj would also have to be static ;) . A mutex would be unnecessarily heavy in this case, you'd be better going with a critical section.

But 4 as already stated, you can't guarantee threadsafe 3 lazy-initialisation without using at least 2 one synchronisation primitive.

Edit: Yup 1 Derek, you're right. My bad. :)

Score: 1

You could use Matt's solution, but you'd 14 need to use a proper mutex/critical section 13 for locking, and by checking "pObj 12 == NULL" both before and after the 11 lock. Of course, pObj would also have to 10 be static ;) . A mutex would be unnecessarily 9 heavy in this case, you'd be better going 8 with a critical section.

OJ, that doesn't 7 work. As Chris pointed out, that's double-check 6 locking, which is not guaranteed to work 5 in the current C++ standard. See: C++ and the Perils of Double-Checked Locking

Edit: No 4 problem, OJ. It's really nice in languages 3 where it does work. I expect it will work 2 in C++0x (though I'm not certain), because 1 it's such a convenient idiom.

Score: 1
  1. read on weak memory model. It can break 15 double-checked locks and spinlocks. Intel 14 is strong memory model (yet), so on Intel 13 it's easier

  2. carefully use "volatile" to avoid 12 caching of parts the object in registers, otherwise 11 you'll have initialized the object pointer, but 10 not the object itself, and the other thread 9 will crash

  3. the order of static variables 8 initialization versus shared code loading 7 is sometimes not trivial. I've seen cases 6 when the code to destruct an object was 5 already unloaded, so the program crashed 4 on exit

  4. such objects are hard to destroy 3 properly

In general singletons are hard to 2 do right and hard to debug. It's better 1 to avoid them altogether.

Score: 0

I suppose saying don't do this because it's 6 not safe and will probably break more often 5 than just initializing this stuff in main() isn't 4 going to be that popular.

(And yes, I know 3 that suggesting that means you shouldn't 2 attempt to do interesting stuff in constructors 1 of global objects. That's the point.)

More Related questions