Wednesday, November 22, 2006

Function static variables in multi-threaded environments

The instantiation process of function static variables isn't necessarily what you expect it to be. Raymond Chen posted a nice bit on this some time back (http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx). If you haven't read it, go do so now. I won't repeat it here.

At this point, there's a 90% chance that you've actually skipped the recommended read, so I'll go ahead and sum it up real quick. Yeah I know I said I wouldn't. I lied. Get on with your life. Anyhoo, here goes the five second recap ..

Consider the following function:

void foo()
{
static int x = calcSomething();
}
It seems simple enough, and it is. The static variable will be initialized once, based on the result of the function calcSomething. With non-volatile constant values, the compiler can optimize the generated code to use the memory address of the value. In this case, where a function is called, a function we know nothing about might I add, it doesn't necessarily have that luxury. Looking at the generated assembly code, we'll see something like this

mov     eax,1 
test byte ptr [$S1],al
jne foo+1Dh
or dword ptr [$S1],eax
call calcSomething
mov dword ptr [x)],eax
Loosely translated to pseudo C++, this will be

void foo()
{
static bool x_set = false;
static int x;
if(!x_set)
{
x_set = true;
x = calcSomething();
}
}
As you can see, there's no interlocking code here. This essentially means that function will be anything but thread safe. One thread may reach, but not execute, x_set = true, only to be swapped out in favor of another thread which does the same. The result would be that calcSomething is executed two or more times -- which is likely to be a bad thing.

That's it for the recap. Now, if we'd like to fix this problem, what comes to mind? Interlocking, obviously...

This article can now be found at http://www.codeguru.com/cpp/misc/misc/threadsprocesses/article.php/c12897/.

1 kommentarer:

Jerome said...

Thread-local static variables implemented at the compiler level may actually be a better solution. The memory overhead is likely to be minimal, while the performance hit from interlocking could be very substantial.