I have come across C++03 some code that takes this form:
struct Foo {
int a;
int b;
CRITICAL_SECTION cs;
}
// DoFoo::Foo foo_;
void DoFoo::Foolish()
{
if( foo_.a == 4 )
{
PerformSomeTask();
EnterCriticalSection(&foo_.cs);
foo_.b = 7;
LeaveCriticalSection(&foo_.cs);
}
}
Does the read from foo_.a need to be protected? e.g.:
void DoFoo::Foolish()
{
EnterCriticalSection(&foo_.cs);
int a = foo_.a;
LeaveCriticalSection(&foo_.cs);
if( a == 4 )
{
PerformSomeTask();
EnterCriticalSection(&foo_.cs);
foo_.b = 7;
LeaveCriticalSection(&foo_.cs);
}
}
If so, why?
Please assume the integers are 32-bit aligned. The platform is ARM.
Technically yes, but no on many platforms. First, let us assume that
intis 32 bits (which is pretty common, but not nearly universal).It is possible that the two words (16 bit parts) of a 32 bit
intwill be read or written to separately. On some systems, they will be read separately if theintisn’t aligned properly.Imagine a system where you can only do 32-bit aligned 32 bit reads and writes (and 16-bit aligned 16 bit reads and writes), and an
intthat straddles such a boundary. Initially theintis zero (ie,0x00000000)One thread writes
0xBAADF00Dto theint, the other reads it “at the same time”.The writing thread first writes
0xBAADto the high word of theint. The reader thread then reads the entireint(both high and low) getting0xBAAD0000— which is a state that theintwas never put into on purpose!The writer thread then writes the low word
0xF00D.As noted, on some platforms all 32 bit reads/writes are atomic, so this isn’t a concern. There are other concerns, however.
Most lock/unlock code includes instructions to the compiler to prevent reordering across the lock. Without that prevention of reordering, the compiler is free to reorder things so long as it behaves “as-if” in a single threaded context it would have worked that way. So if you read
athenbin code, the compiler could readbbefore it readsa, so long as it doesn’t see an in-thread opportunity forbto be modified in that interval.So possibly the code you are reading is using these locks to make sure that the read of the variable happens in the order written in the code.
Other issues are raised in the comments below, but I don’t feel competent to address them: cache issues, and visibility.