Is static init thread-safe with VC2010?
From Visual Studio 2010's documentation on Static:
Assigning a value to a static local variable in a multithreaded application is not thread safe and we do not recommend it as a programming practice.
The second part of your question has some good existing answers.
Updated Nov 22, 2015:
Others have verified, specifically, that static initialization is not thread safe either (see comment and other answer).
User squelart on VS2015:
you may want to add that VS2015 finally gets it right: https://msdn.microsoft.com/en-au/library/hh567368.aspx#concurrencytable ("Magic statics")
The following code snippet shows "locally scoped static object initialisation" is NOT thread-safe:
#include <windows.h>#include <stdio.h>#include <process.h>struct X { ~X() { puts("~X()"); } int i_ ; void print(void) { printf("thread id=%u, i = %d\n", GetCurrentThreadId(), i_); } X(int i) { puts("begin to sleep 10 seconds"); Sleep(1000 * 10); i_ = i; printf("X(int) i = %d\n", i_); puts("end"); }};X & getX(){ static X static_x(1000); return static_x;}void thread_proc(void *){ X & x = getX(); x.print();}int main(int argc, char *argv[]){ HANDLE all_threads[2] = {}; all_threads[0] = HANDLE( _beginthread(thread_proc, 0, 0) ); printf("First thread Id: %u\n", GetThreadId(all_threads[0]) ); Sleep(1000); all_threads[1] = HANDLE( _beginthread(thread_proc, 0, 0) ); printf("Second thread Id: %u\n", GetThreadId(all_threads[1]) ); WaitForMultipleObjects( _countof(all_threads), all_threads, TRUE, 1000 * 20); puts("main exit"); return 0;}
The output will be(of course thread id will be different on your machine):
First thread Id: 20104begin to sleep 10 secondsSecond thread Id: 20248thread id=20248, i = 0X(int) i = 4247392endthread id=20104, i = 1000main exit~X()
Before the first thread returns which means the singleton's ctor is called and returned, the second thread get the un-initialized object and call it's member method(because the static object is in BSS segment, it'll be initilized to zero after loader load the executable) and get the wrong value: 0.
Turning on assembly listing by /FAsc /Fastatic.asm will get the assembly code for function getX():
01: ?getX@@YAAAUX@@XZ PROC ; getX02: 03: ; 20 : {04: 05: 00000 55 push ebp06: 00001 8b ec mov ebp, esp07: 08: ; 21 : static X static_x(1000);09: 10: 00003 a1 00 00 00 00 mov eax, DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA11: 00008 83 e0 01 and eax, 112: 0000b 75 2b jne SHORT $LN1@getX13: 0000d 8b 0d 00 00 0014: 00 mov ecx, DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA15: 00013 83 c9 01 or ecx, 116: 00016 89 0d 00 00 0017: 00 mov DWORD PTR ?$S1@?1??getX@@YAAAUX@@XZ@4IA, ecx18: 0001c 68 e8 03 00 00 push 1000 ; 000003e8H19: 00021 b9 00 00 00 00 mov ecx, OFFSET ?static_x@?1??getX@@YAAAUX@@XZ@4U2@A20: 00026 e8 00 00 00 00 call ??0X@@QAE@H@Z ; X::X21: 0002b 68 00 00 00 00 push OFFSET ??__Fstatic_x@?1??getX@@YAAAUX@@XZ@YAXXZ ; `getX'::`2'::`dynamic atexit destructor for 'static_x''22: 00030 e8 00 00 00 00 call _atexit23: 00035 83 c4 04 add esp, 424: $LN1@getX:25: 26: ; 22 : return static_x;27: 28: 00038 b8 00 00 00 00 mov eax, OFFSET ?static_x@?1??getX@@YAAAUX@@XZ@4U2@A29: 30: ; 23 : }
At line 10 the cryptic symbol [?$S1@?1??getX@@YAAAUX@@XZ@4IA] is the global indicator(also in BSS) which flags whether the singleton is ctored or not, it will be flaged as true by line 14-17, just before calling into the ctor, that's the problem, this also explains why the second thread immediately got the un-initialized singleton object and happily call it's member function. There's no thread-safety related code inserted by the compiler.