What can go wrong if your compiler is not thread aware
Courtesy of Pete Zaitcev, here's a great example of what happens when optimizing compilers aren't thread aware.
Start with code of the form:
struct a b;b.pos = *ppos; ret = foo(&b, ..., b.pos);
Modern versions of gcc 4 on x86 will optimize the function call into:
ret = foo(&b, ..., *ppos);
(This is a less stupid optimization than it looks;
ppos is a function
parameter and I believe it's in a register, so it may well be faster to
perform an indirect load from it than a computed indirect load off the
What goes wrong in a multi-threaded environment (in this case, the Linux
kernel) is that the value of
*ppos can change between the store
b.pos and the function call, and that the
foo function expects
the two values to be the same. Of course, the authors of the function
that this appeared in didn't think that they needed to do any locking,
because after all they only dereference
(To deal with one possible code nitpick, the Linux kernel makes liberal use of implicit atomic reads and writes. This probably makes purists cringe, but the odds of a major CPU architecture ever not having large atomic reads and writes are pretty small by now.)
I don't think we can blame either the compiler or the programmers for this. What's really happening is that we've been tripped up because we have different implicit assumptions about the code than the compiler does. And a good part of the reason that these sorts of assumptions stay implicit is that we don't have good tools for making them explicit in non-annoying ways.
(So while we can blithely talk about a 'thread aware compiler', I'm not sure we know what one should actually look like.)
Comments on this page:Written on 29 December 2006.