I've been in a debate about a corner case regarding local variables in a multithread environment.
The question is regarding programs formed like:
std::mutex mut;
int main()
{
std::size_t i = 0;
doSomethingWhichMaySpawnAThreadAndUseTheMutex();
mut.lock();
i += 1; // can this be reordered?
mut.unlock();
return i;
}
The question revolves around whether the i += 1
can be reordered to occur above the mutex locking.
The obvious parts are that mut.lock()
happens-before i += 1
, so if any other thread might be able to observe the value of i
, the compiler is obliged to not have incremented it. From 3.9.2.3 of the C++ spec, "If an object of type T is located at an address A, a pointer of type cv T* whose value is the address A is said to point to that object, regardless of how the value was obtained."" This means that if I used any means to get a pointer to i
, I can expect to see the right value.
However, the spec does state that the compiler may use the "as-if" rule to not give an object a memory address (footnote 4 on section 1.8.6). For example, i
could be stored in a register, which has no memory address. In such a case, there would be no memory address to point to, so the compiler could prove that no other thread could access i
.
The question I am interested in is what if the compiler does not do this "as-if" optimization, and does indeed store the object. Is the compiler permitted to store i
, but do reordering as-if i
was not actually stored? From an implementation perspective, this would mean that i
might be stored on a stack, and thus it would be possible to have a pointer point at it, but have the compiler assume nobody can see i
, and do the re-order?