I\'m a bit confused about the purpose of std::call_once
. To be clear, I understand exactly what std::call_once
does, and how to use it. It\'
Slight variation on standard C++ solution is to use lambda inside the usual one:
// header.h
namespace dbj_once {
struct singleton final {};
inline singleton & instance()
{
static singleton single_instance = []() -> singleton {
// this is called only once
// do some more complex initialization
// here
return {};
}();
return single_instance;
};
} // dbj_once
Please observe
If you read this you'll see that std::call_once
makes no guarantee about data-races, it's simply a utility function for performing an action once (which will work across threads). You shouldn't presume that is has anything close to the affect of a mutex.
as an example:
#include <thread>
#include <mutex>
static std::once_flag flag;
void f(){
operation_that_takes_time();
std::call_once(flag, [](){std::cout << "f() was called\n";});
}
void g(){
operation_that_takes_time();
std::call_once(flag, [](){std::cout << "g() was called\n";});
}
int main(int argc, char *argv[]){
std::thread t1(f);
std::thread t2(g);
t1.join();
t2.join();
}
could print both f() was called
and g() was called
. This is because in the body of std::call_once
it will check whether flag
was set then set it if not then call the appropriate function. But while it is checking or before it set flag
another thread may call call_once
with the same flag and run a function at the same time. You should still protect calls to call_once
with a mutex if you know another thread may have a data race.
I found a link to the proposal for the std::call_once
function and thread library which states that concurrency is guaranteed to only call the function once, so it should work like a mutex (y)
More specifically:
If multiple calls to call_once with the same flag are executing concurrently in separate threads, then only one thread shall call func, and no thread shall proceed until the call to func has completed.
So to answer your question: yes, other threads will be blocked until the calling thread returns from the specified functor.
One thing that call_once
does for you is handle exceptions. That is, if the first thread into it throws an exception inside of the functor (and propagates it out), call_once
will not consider the call_once
satisfied. A subsequent invocation is allowed to enter the functor again in an effort to complete it without an exception.
In your example, the exceptional case is also handled properly. However it is easy to imagine a more complicated functor where the exceptional case would not be properly handled.
All this being said, I note that call_once
is redundant with function-local-statics. E.g.:
CSingleton& CSingleton::GetInstance()
{
static std::unique_ptr<CSingleton> m_instance(new CSingleton);
return *m_instance;
}
Or more simply:
CSingleton& CSingleton::GetInstance()
{
static CSingleton m_instance;
return m_instance;
}
The above is equivalent to your example with call_once
, and imho, simpler. Oh, except the order of destruction is very subtly different between this and your example. In both cases m_instance
is destroyed in reverse order of construction. But the order of construction is different. In your m_instance
is constructed relative to other objects with file-local scope in the same translation unit. Using function-local-statics, m_instance
is constructed the first time GetInstance
is executed.
That difference may or may not be important to your application. Generally I prefer the function-local-static solution as it is "lazy". I.e. if the application never calls GetInstance()
then m_instance
is never constructed. And there is no period during application launch when a lot of statics are trying to be constructed at once. You pay for the construction only when actually used.