Re: Threadsafe singletons
David Barrett-Lennard wrote:
kanze wrote:
David Barrett-Lennard wrote:
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?
AFAIK VC++ never (silently) adds synchronization primitives to
your code.
I wouldn't know. I don't know what threading guarantees it
gives.
Can you give me an example of where a compiler introduces an
unexpected static?
Plenty. On the old 8080, almost any time the compiler spilled
registers; indirection to the stack was so expensive that one
compiler I used allocated all local variables statically, added
a counter to detect recursion, and only copied the local
variables to the stack when you actually recursed. It's not
inconceivable that some modern compilers do this as well, for
some strange architectures.
In the case of g++ pre-3.0, the compiler used static variables
in its implementation of stack walkback, at least on a Sparc.
Two threads throw an exception at the same time, and strange
things happened.
In general, unless the compiler makes some formal guarantee, you
can't count on it working.
If I can write:
void
f()
{
static int const i = 42 ;
// ...
}
and expect it to work
Why expect that to be threadsafe?
Because Posix requires it in C. It's hard to imagine an
implementation which would make this thread safe in C, but not
in C++.
In practice, the requirements of the C++ standard pretty much
mean that a non-thread-safe implementation is not possible.
?3.6.2/1: "Objects of POD types with static storage duration
initialized with constant expressions shall be initialized
before any dynamic initialization takes place." Theoretically,
I guess you could arrange for pthread_create to be called before
any dynamic initialization takes place, but practically, the
compiler can't recognize the case if you, and even if it could,
it wouldn't go to the bother of generating special code just to
break your program. (In all implementations I'm aware of, i is
initialized as part of loading the image from disk, before any
user code, or even the compiler's own startup code has had a
chance to run.)
The compiler isn't required to optimize the variable out of
existence is it? Assuming it initializes it on first access,
you should expect a potential racing condition.
But the C++ standard doesn't allow it to wait until first access
to initialize it. (Except, I guess, under the "as if" rule.
There's no way you could see the value in a conforming program
before calling f. But in practice, it doesn't happen.)
Furthermore, even if you ensure one thread calls f() before
any other threads, you will need to ensure memory barriers are
used appropriately.
Both pthread_create and its equivalent in Windows take care of
that.
[...]
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.
But your singleton destructors never run!
Right. Which is exactly what I want.
As a general rule my programs only do observable things within
the scope of main(). Threads, files, sockets, windows etc are
only opened and closed within the scope of main(). Therefore
I don't do anything important in singleton destructors. You
evidently don't either because you don't destruct them at all.
In the case of my singleton log, I would do something important
in the destructor if I called it. Something I don't want to do
before the last user written code has run. (And something that
isn't necessary to do, since the system will take care of any
necessary clean-up: close the file, free the memory, etc.)
Conceptually, it might be cleaner to say somehow that running
this destructor must be the very last thing you do, but neither
C++ nor any of the implementations I know have anything to
support this. And in practice, letting the system do it works
just as well.
I use assertions to trap attempts to log messages after the
log file has been explicitly closed from main(), unless the
log file is a debugging aid for the programmer.
Which is almost always the case.
In the latter case it logically needs to outlive the scope of
main(). Only in that case would I consider allowing the
singleton to leak and assume the system closes the log file
for me.
I find it convenient to know that my programs (should) never
have any memory leaks. VC++ can dump all the leaks in the
output pane. It's nice for this to be empty unless there is a
programming error.
Well, the memory checker I use (Purify) will not display a
positive memory leak if there is a pointer to the object in
static memory. It will indicate a "potential memory leak", but
even that can be masked out (on a one by one basis, or
globally).
The crufty, hand-built checker I use at home also allows masking
out allocations in the creation of such never-to-be destructed
objects, although in this case, you do have to modify the code,
to tell the checker that it shouldn't check allocations in this
particular span of code; it does so by constructing a special
object on the stack, and it would be fairly simple to provide a
dummy object for the cases where the memory checker wasn't
linked in.
If you're memory checker doesn't have support for this sort of
thing, I'd suggest you get something better.
--
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! ]