Reputation: 11
#include<iostream>
using namespace std;
class singleton
{
private:
static singleton* ref;
singleton()
{
cout<<"singleton ctor"<<endl;
}
public:
static singleton* getInstance()
{
return ref;
}
};
singleton* singleton::ref=new singleton();
int main()
{
singleton* ref=singleton::getInstance()
}
Upvotes: 1
Views: 134
Reputation: 153792
There are no threads in your example. Assuming there are more objects initialized during static initialization which may be spawning threads and access singleton::ref
, the code is prone to access uninitialized memory and it isn't thread-safe. If the first thread is started after entering main()
the code is thread-safe.
In case you want to make sure the object is properly constructed upon first access and, at the same time, make the construction thread-safe even when accessed during static initialization from multiple threads, you'd use
singleton* singleton::getInstance() {
static singleton rc;
return &rc;
}
The relevant section in the standard which guarantees that the above is thread safe is 6.7 [stmt.dcl] paragraph 4:
... If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization ...
The object would be constructed the first time getInstance()
is called. Even if multiple threads concurrently call getInstance()
the object will be constructed only once and the concurrent calls to getInstance()
will block until construction is complete. You can make sure that the construction of getInstance()
happens during static initialization by using it in the implementation of singleton
:
static singleton* constructionDummy = singleton::getInstance();
Note that singletons generally cause major problems, doubly so in multi-threaded programs. In addition to the problems singletons already create in single-threaded programs they further introduce the potential for data races and, in an attempt to address the data races, tend to introduce serialization. With the possible exception of singletons which set up strictly immutable data I recommend to not use them.
Upvotes: 3
Reputation: 135
While as a general principle I agree with the point of view that creating objects in well defined order is preferable, from thread safety perspective the implementation posted in the question is safer than constructing the singleton object within getInstance. In the latter case unless the programmer explicitly use atomic operations, the contructor can, in theory be invoked more than once. Following is dis-assembly using VS2013 (debug with no optimizations) for x86 target for this code.
singleton* singleton::getInstance() {
static singleton rc;
return &rc;
}
static singleton* getInstance()
{
00E85820 push ebp
00E85821 mov ebp,esp
00E85823 push 0FFFFFFFFh
00E85825 push 0E89E6Eh
00E8582A mov eax,dword ptr fs:[00000000h]
00E85830 push eax
00E85831 sub esp,0C0h
00E85837 push ebx
00E85838 push esi
00E85839 push edi
00E8583A lea edi,[ebp-0CCh]
00E85840 mov ecx,30h
00E85845 mov eax,0CCCCCCCCh
00E8584A rep stos dword ptr es:[edi]
00E8584C mov eax,dword ptr ds:[00E8F000h]
00E85851 xor eax,ebp
00E85853 push eax
00E85854 lea eax,[ebp-0Ch]
00E85857 mov dword ptr fs:[00000000h],eax
static singleton ref;
00E8585D mov eax,dword ptr ds:[00E8F330h]
00E85862 and eax,1
00E85865 jne singleton::getInstance+6Ch (0E8588Ch)
00E85867 mov eax,dword ptr ds:[00E8F330h]
00E8586C or eax,1
00E8586F mov dword ptr ds:[00E8F330h],eax
00E85874 mov dword ptr [ebp-4],0
00E8587B mov ecx,0E8F32Ch
00E85880 call singleton::singleton (0E810D2h)
00E85885 mov dword ptr [ebp-4],0FFFFFFFFh
return &ref;
00E8588C mov eax,0E8F32Ch
}
dword ptr ds:[00E8F330h] is used as flag to check whether to call constructor or not.
As expected , no atomic compare and exchange instructions are emitted by the compiler. Therefore, if thread B executes instruction at 00E8585D before thread A executes instruction 00E8586F, the constructor will be invoked by both. However, to see this in practice, we would probably need to use special test cases where multiple threads are spawned (on a multicore processor) and blocked on an event/semaphore, . Then signal the semaphore to release all thread at once, and hopefully we will see the constructor will be called more than once. The same applies if you do a new singleton() inside getInstance.
This issue is not specific to singletons.
Upvotes: 0
Reputation: 22981
If you mean by thread safe that different threads will get the correct (same) pointer by calling singleton::getInstance
, than: yes, it is thread safe.
And (as pointed out in the comment) you should create all you non trivial static variables in a well-defined order in the main function (or another function)!
So write this:
singleton* singleton::ref = NULL;
int main()
{
singleton::ref = new singleton();
// create objects which use the singleton
singleton* ref = singleton::getInstance();
// create threads...
}
Upvotes: -1