Re: Threadsafe singletons
David Barrett-Lennard wrote:
kanze wrote:
David Barrett-Lennard wrote:
kanze wrote:
David Barrett-Lennard wrote:
[...]
At least some compilers, in a threaded environment, use
something like pthread_once to initialize the variable, so
constructing it before entering main isn't necessary.
Good point. I didn't know some compilers would do that.
Only some. In my opinion, it should be required, once the
C++ standard recognizes threads, but there are valid
arguments both ways: a function with a static variable is
inherently not thread safe, and presumably will be used only
in a single threaded environment, or when the client code
has provided higher level protection. In such cases, there
is no threading issue in the code, and the use of something
like pthread_once is extra overhead---you're paying for
something you don't use. (But I'm still in favor of doing
the right thing.)
I think the compiler should keep out of such concerns!
Particularly now that most compilers don't give thread safety.
The last compiler I used that didn't guarantee thread safety was
g++ 2.95.2. If a compiler doesn't guarantee thread safety, you
can't use it for multithreaded code. It's as simple as that.
How do you know that it doesn't use static variables internally?
If I can write:
void
f()
{
static int const i = 42 ;
// ...
}
and expect it to work, I should also be able to write:
void
f()
{
static std::string const s = "42" ;
// ...
}
Where's the logical difference?
The cat's out of the bag!
It is already.
At the other extreme, other compilers document nothing, and
may use some technique which isn't thread safe. In the
absense of any specific guarantees, I prefer to avoid
counting too much on what the compiler does here.
My own technique is basically:
static MySingleton* ourInstance = &MySingleton::instance() ;
MySingleton&
MySingleton::instance()
{
if ( ourInstance == NULL ) {
ourInstance = new MySingleton ;
}
return *ourInstance ;
}
How is it deleted?
It isn't. Generally, I don't want it to be; deleting it leads
to problems in the ordering of destructors (supposing it is used
in the destructor of an object with static lifetime).
I have never had an order of destruction problem myself.
You've never had one, or you've never had one that hadn't been
solved by someone else already. Have you ever used std::cerr in
the destructor of a static object? How do you know that it
hadn't been destructed already? The standard has taken special
precautions to ensure that it will not be destructed.
However I use singletons rarely and they tend to be used
either for caching or for registries. Can you outline a
reasonable example with order of destruction problems?
A singleton log? I can imagine wanting to log in a destructor.
More generally, as a matter of principle. The goal is that a
user can use a singleton at any time, without worry, and it will
be there. He's independant of order of construction issues, but
also of order of destruction.
In Windows if a DLL containing the definition of MySingleton
is loaded after main() begins with a call to LoadLibrary(),
then AFAIK MySingleton::GetInstance() will be called before
LoadLibrary() returns because of the static initialisation.
So the singleton is fully constructed before any attempt is
made to bind a function call to MySingleton::GetInstance().
Attention: fully constructed isn't enough. There's also a
question of memory synchronization---are other threads
guaranteed to see the version the thread which called
LoadLibrary() sees?
Unfortunately I doubt whether Microsoft documentation will
say!
In which case, you have to assume not. (Posix documentation
says that there is no guarantee that they will---dlopen is NOT
in the list of the functions which guarantee memory
synchronization.)
Under Posix (and I'm pretty sure under Windows as well), all
thread related system requests guarantee memory
synchronization, so if you use system requests to
communicate the availability of the new service to other
threads, you are safe. The same may not be true for certain
lock-free algorithms.
If it is mutative, and represents the correct level of
granularity for locking, another alternative is to
acquire the lock before checking for null, and to return
a boost::shared_ptr to the object, whose "destructor"
frees the lock. You need one lock anyway, this avoids a
second.
I presume I misunderstand you because it seems like you
will have a problem accessing an uninitialised lock if the
singleton is accessed before main() begins.
Mutexes can be initialilzed statically, at least under
Posix. And static initialization takes place before any
dynamic initialization.
Note that Win32 events, mutexes, critical sections etc cannot
be initialized statically under Windows.
That's bad:-(. I guess you're stuck using a singleton for the
mutex or critical section.
For example, I quote from MSDN
"The process is responsible for allocating the memory used by
a critical section object, which it can do by declaring a
variable of type CRITICAL_SECTION. Before using a critical
section, some thread of the process must call the
InitializeCriticalSection or
InitializeCriticalSectionAndSpinCount function to initialize
the object."
Which makes it sound like CRITICAL_SECTION is a POD. So
presumably, they could have designed a static initializer for
it. (From experience, it's not too hard to design this sort of
thing so that zero initialization is sufficient. It is for the
mutexes of Solaris, for example, although I don't believe that
this is either documented nor guaranteed.) Dynamic
initialization only becomes necessary if you have need some
attributes at other than their default value.
Alternatively, use a singleton as above to create the mutex.
(But I really can't imagine a system where mutexes can't be
initialized statically. It sounds like a real recepe for
order of initialization problems.)
Yep. Sounds like Microsoft missed the boat on this one.
--
James Kanze GABI Software
Conseils en informatique orient?e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S?mard, 78210 St.-Cyr-l'?cole, France, +33 (0)1 30 23 00 34
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]