i have implemented a singleton (static version) in C++. I know all the controversy about this pattern and potential thread-safety issues, but i am curious why this exact implementation won't halt. The program never quits, it remains in a deadlock state at the end.
singleton.h:
#pragma once
#include <thread>
#include <atomic>
class Singleton
{
public:
static Singleton& getInstance();
private:
std::thread mThread;
std::atomic_bool mRun;
Singleton();
~Singleton();
void threadFoo();
};
singleton.cpp
#include "singleton.h"
Singleton& Singleton::getInstance()
{
static Singleton instance;
return instance;
}
Singleton::Singleton()
{
mRun.store(true);
mThread = std::thread(&Singleton::threadFoo, this);
}
Singleton::~Singleton()
{
mRun.store(false);
if(mThread.joinable())
mThread.join();
}
void Singleton::threadFoo()
{
while(mRun)
{
}
}
main.cpp
#include "singleton.h"
int main()
{
Singleton::getInstance();
return 0;
}
What I already know:
- the thread terminates
- the main thread is stuck in the join
- it has something to do with the static, if i make the constructor public and create an instance of Singleton in main() it will correctly terminate.
Using Visual Studio 2012. Thanks for your advice.
Ok thank you all for your hints. Apparently this pattern implementation results in a deadlock on VC++.
After doing some further research i found this implementation based on C++11 mechanics which is working in VC++.
singleton.h
singleton.cpp
UPDATE
It looks like Microsoft is aware of this issue. In the VC++ forums a user named "dlafleur" reported this post: https://connect.microsoft.com/VisualStudio/feedback/details/747145
It seems to be fixed in Visual Studio 2015 and above, at least for this specific example.
On the main thread, after
main()
terminates, the CRT acquires the exit lock and calls your static instance destructor, which waits for your background thread to exit.On the background thread, after your thread function terminates, the CRT attempts to acquire the exit lock to do some thread termination work. This blocks forever because the exit lock is held by the main thread, which is waiting for this thread to exit.
It's a simple deadlock that's caused by the CRT implementation. The bottom line is that you can't await thread termination in a static instance destructor on Windows.
I've traced it down to
void __cdecl _lock(int locknum)
insidemlock.c
. Whenmain()
ends, the main thread goes there and enters critical sectionEnterCriticalSection( _locktable[locknum].lock );
. Then Singleton destructor gets called and the other thread tries to enter the same critical section, but can't, and so it starts waiting for main thread to leave the critical section. Main thread, in turn, waits for the other thread. So I guess it's a bug.This deadlock bug is the same as in
std::thread::join() hangs if called after main() exits when using VS2012 RC
and it is not fixed in Visual Studio 2013.
See [basic.start.term] in the Standard: