Re: Double checked locking pattern article on aristeia
on Fri Oct 21 2011, bob bob <r_boghean-AT-hotmail.com> wrote:
1. In accordance with the C++03 standard, the compiler optimizer is
completely free to move both pInstance reads to *before* the Lock
construction -- essentially negating the value of the mutex.
(FYI, I don't see that text anywhere in
http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf... oh... the
text you quoted was from a poster here... well...)
There's clearly something I'm not understanding here. Wouldn't a
mutex lock/unlock imply a memory barrier of some sort?
Only in C++11 where "memory barrier" is a meaningful concept. Within
the memory model of C++03 there was basically no way to express the
kinds of ordering restrictions suitable for multithreading.
If the compiler can move operations across mutex lock/unlock
boundaries that would basically render them useless would it not?
Yes, basically. There are some (expert-only) uses for "half-barriers"
that allow movement of some operations, but mutex locks are basically
always associated with strict, sequentially-consistent barriers.
Say under VC++ doesn't boost::mutex call the MemoryBarrier() macro (or
some equivalent) deep within its bowels?
Something like that. It's just that *according to the C++03 standard*
the compiler has no obligation to respect such a request. Presumably if
they ship working threading primitives, MS has, explicitly or
implicitly, been making stronger guarantees than the standard requires
in that area for years.
Along the same lines why does the code presented by Meyers and
Alexandresc require 2 barriers(one inside the lock, and one outside).
The one inside the lock is clearly to ensure a 'hard sequence
point' (as they call it) to ensure the object is fully constructed
prior to being assigned to the pointer. But I'm not sure what the
second barrier prevents. I guess this would lead back to my original
assumption that mutex lock/unlocks are barriers themselves.
I think you'd better quote the specific code you're asking about... oh,
do you mean this?
--8<---------------cut here---------------start------------->8---
Singleton* Singleton::instance ()
{
Singleton* tmp = pInstance;
... // insert memory barrier
if (tmp == 0) {
Lock lock;
tmp = pInstance;
if (tmp == 0) {
tmp = new Singleton;
... // insert memory barrier
pInstance = tmp;
}
}
return tmp;
}
--8<---------------cut here---------------end--------------->8---
Well, it's a little bit hard to say what they're assuming, because under
C++03 you don't have any standard-portable right to expect something
called a "memory barrier" to work, even if you could lay your hands on
one.
That said, I *think* the point here is that without the barriers, no
effects that are formally "visible" according to the standard force the
boundaries of the lock to exclude the read of pInstance or to include
the write... but I would ask the authors to explain this in more detail
if I were you.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]