Consider your code:
void f(int myid) {
while(1){
while(x!= myid){}
//cout<<"thread : "<< myid<<"\n";
//this_thread::sleep_for(std::chrono::duration(3s));
x = (x % 3) + 1;
}
}
If the program didn't have undefined behaviour, then you could expect that when f
was called, x
would be read from the stack at least once, but having done that, the compiler has no reason to think that any changes to x
will happen outside the function, or that any changes to x
made within the function need to be visible outside the function until after the function returns, so it's entitled to read x
into a CPU register, keep looking at the same register value and comparing it to myid
- which means it'll either pass through instantly or be stuck forever.
Then, compilers are allowed to assume they'll make progress (see Forward Progress in the C++ Standard), so they could conclude that because they'd never progress if x != myid
, x
can't possibly be equal to myid
, and remove the inner while
loop. Similarly, an outer loop simplified to while (1) x = (x % 3) + 1;
where x
might be a register - doesn't make progress and could also be eliminated. Or, the compiler could leave the loop but remove the seemingly pointless operations on x
.
Putting your code into the online Godbolt compiler explorer and compiling with GCC trunk at -O3
optimisation, f(int) code is:
f(int):
.L2:
jmp .L2
If you make x
atomic, then the compiler can't simply use a register while accessing/modifying it, and assume that there will be a good time to update it before the function returns. It will actually have to modify the variable in memory and propagate that change so other threads can read the updated value.