Quote:
> > Not safe. Such a singleton can't be used in global objects'
> > constructors, because m_instanceCs may not have been constructed
yet.
> <emphasis>
> > Allowing such usage is the whole point of having a static variable
> > inside GetInstance function (and the source of its thread-unsafety)
> > rather than making it a static member variable.
> </emphasis>
> Not sure I agree with the emphasized assessment above.
> Global objects (AFAIK) construct on the same thread, so there are no
> threading issues if this is, indeed, "the whole point". (Any threads
started
> by those global objects are another matter entirely.)
Well, here's the situation. If you implement the singleton naively, like
this:
class Singleton
{
private:
static Singleton instance;
public:
static Signleton& getInstance() {return instance;}
Quote:
};
it breaks for a single-threaded program where global objects try to use
the singleton in their constructors. The usual way to solve this problem
is like this:
class Singleton
{
public:
static Signleton& getInstance()
{static Signleton instance; return instance;}
Quote:
};
but then it breaks in a program where there are no global objects, but
two threads try to initialize the singleton at the same time. Thus
either implementation is unsafe in a reusable library.
I know two ways around this. One is the technique used to ensure that
std::cout is available to all global objects. You put the following into
the same .h file that defines the Singleton class:
namespace
{
struct SingletonInit
{
SingletonInit() {Singleton::getInstance();}
} initSingleton;
Quote:
};
This forces a getInstance call in every translation unit that might use
the Singleton. A (minor) disadvantage is that the singleton instance
gets initialized even if it is never used after all.
Another way is using OS-specific atomic integer operations, just as you
noted. Luckily, a global variable of a fundamental type gets its intial
value assigned before any code whatsoever is executed, so you can rely
on it being, say, 0 when the object is not initialized. You can then use
a manually implemented spinlock for synchronization.
--
With best wishes,
Igor Tandetnik
"For every complex problem, there is a solution that is simple, neat,
and wrong." H.L. Mencken