Re: local static variable (just a curiosity, btw)
Mycroft Holmes wrote:
Hi,
recently we found a concurrency error caused by code like this:
struct myClass
{
static DWORD getTheGlobalConstant()
{
struct perform
{
static DWORD computation()
{
// compute the result
// this takes some time
// but the result is always the same
// e.g. assume we compute the 123456th prime :)
return result;
}
};
static const DWORD x = perform::computation();
return x;
Under the hood, that is going to be something like:
//both are statically initialized:
static const bool __x_is_initialized = false;
static const DWORD x = 0;
//dynamic initialization:
if (!__x_is_initialized)
{
__x_is_initialized = true; //1
x = perform::computation(); //2
}
return x;
}
getTheGlobalConstant is invoked by several threads at a time: it was not
made thread-safe on purpose, since we don't care about x being computed more
than once, as it always has the same value. however, occasionally the return
value is 0, so x is returned before it's ever been computed (we know it for
sure, because we divide by x and we got an integer division by 0).
is this possible?
Well, if you look at the code above, a thread might read
__x_is_initialized as true but still get the default value for x. Even
if you switch around lines 1 and 2, the compiler or CPU might reorder
the writes to the two static variables, since such a reordering is valid
for a single thread in the absense of any memory barriers.
we thought about two workarounds: the first is declare a global constant
const DWORD triggerAssignment = myclass:: getTheGlobalConstant();
That will work, since the initialization should be done prior to
starting your other threads.
but then another proposal came out:
static const volatile DWORD x = perform::computation();
while (x == 0) {}
return x;
Would it work?
On VC2005, volatile has memory barrier semantics, and writes to volatile
DWORDs should be atomic, so I think it will work, yes. You might end up
unnecessarily spinning of course. I think I much prefer this alternative
though, since I don't like loops that look infinite:
static const volatile DWORD x = 0;
if (x == 0)
x = perform::computation();
return x;
Tom