Re: Is this safe?
On Jul 1, 7:41 pm, Andre Kaufmann <akfmn...@t-online.de> wrote:
Joshua Maurice wrote:
See "C++ and the Perils of Double Checked Locking"
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
for a thorough description of why the above does not work and has
never worked.
It depends perhaps (?) on the CPU / OS, but I think the synchronization
primitives should work equally on each CPU, since safe multi threaded
code couldn't be written safely without memory fences.
Abstract primitives like mutexes generally have platform independent
semantics, yes.
But to play it safe [for me ;-)] I refer to Windows with a x86 / x64 CPU.
The relevant code, which IMHO makes the code safe (beside perhaps one
detail) is:
AccessLock<CriticalSection> access(key_);
if ( !instance_ )
{
static Singleton<T> theInstance;
instance_ = &theInstance;
}
Let me rewrite the code, how I interpret it to work (internally):
line 0 - if (!instance) {
a) CriticalSection.EnterCriticalSection();
b) if (!instance) {...}
c) CriticalSection.LeaveCriticalSection();
At point a) c) memory barrier instructions are executed, which ensure
that no reordering occurs inside the boundaries and that all changes to
memory are visible to all cores.
Take this scenario: thread X comes along, performs the first check at
line 0, sees the pointer is null, does the memory barrier in at line
A, does the second check at line b, and then executes the
initialization at line b. Thread Y comes along, does the first pointer
check at line 0, sees it not-null, and goes along its merry way, never
executing a memory barrier. Thread Y may then see the object in a
partially constructed state. Specifically, there is a code path which
never executes a memory barrier. To establish a happens-before
relationship, BOTH threads must execute a threading primitive (more
specifically correct threading primitives correctly). Double checked
locking tries to avoid this, to make some threads skip executing
threading primitives, and that is why double checked locking is
broken, because it does not correctly establish the happens-before
relationship as so stressed by the paper.
(In the eager-initialized, initialized-on-demand singleton
implementation, the singleton is initialized before main, and thus
before all other threads. The happens-before relationship is
established by thread_spawn. All writes before spawn in the spawning
thread "happen before" all reads in the spawned thraed.)
What perhaps might be a (although I suppose it to be very unlikely)
problem is the second
Which could be an register operation only, not accessing the memory.
Either a volatile modifier should be added or under Windows an
InterlockedExchange instruction should be used to play it 100% safe.
I'm not exactly sure what you're saying. All I know is that any answer
to a threading question in C or C++ claiming to be portable which
involves "volatile" is automatically wrong. volatile is not, has not,
and probably never will be a threading primitive in C or C++.
Go reread the article, specifically the part about how the standard
does not address threading at all, and how implementers have
interpreted volatile to only be meaningful for a single thread and not
to guarantee visibility to other threads.
Also, it gets even worse. You have static data members of template
classes. It doesn't work the way you want it to work. You have to
define static data members in a single translation unit (read: cpp
file). You cannot declare them in the header file, and you definitely
cannot have "static" in "template<typename T> static Singleton<T>*
Hm, since you can't export templates easily (without using export), how
should a static member template be initialized, if not in the header file
?
[...] whch contains a template instantiation for every argument to the
template used in the entire program.) Short version: avoid static data
For each instance separately ?
I have several compilers which compile such code (initialization of
static template members in the header file) and AFAIK/IIRC the linker is
required to remove all double template instances - I'm not 100% sure
about if the standard requires that too.
No. ODR, One Definition Rule. Each static class member must be defined
exactly once in a program. It cannot be defined in multiple
translation units. That your linker removes duplicate definitions is
probably because it's a general purpose linker for multiple languages,
and other languages (ex: Fortran, and in some cases C) require
removing duplicate definitions.
template export would solve this problem (just define it in a single
cpp and export it), but too bad it'll never be implemented on all
major compilers.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]