Today I came across this question:
you have a code
static int counter = 0;
void worker() {
for (int i = 1; i <= 10; i++)
counter++;
}
If worker
would be called from two different threads, what value will counter
have after both of them are finished?
I know that actually it could be anything. But my internal guts tells me, that counter++
will most likely be translated into single assembler instruction, and if both threads are execute on the same core, counter
will be 20.
But what if those threads are run on different cores or processors, could there be a race condition in their microcode? Is one assembler instruction could always be viewed as atomic operation?
Atomic operations in concurrent programming are program operations that run completely independently of any other processes. Atomic operations are used in many modern operating systems and parallel processing systems.
On a single processor system, if an operation is implemented in a single CPU instruction, it is always atomic. Therefore it is safe to assume that operations like XCHG or INC are atomic on such systems. If an operation requires multiple CPU instructions, then it may be interrupted in the middle of executing.
It would have added complexity in decoding and had probably required one more ALU unit.
A stack is an array-like data structure in the memory in which data can be stored and removed from a location called the 'top' of the stack. The data that needs to be stored is 'pushed' into the stack and data to be retrieved is 'popped' out from the stack.
Specifically for x86, and regarding your example: counter++
, there are a number of ways it could be compiled. The most trivial example is:
inc counter
This translates into the following micro operations:
counter
to a hidden register on the CPUcounter
This is essentially the same as:
mov eax, counter
inc eax
mov counter, eax
Note that if some other agent updates counter
between the load and the store, it won't be reflected in counter
after the store. This agent could be another thread in the same core, another core in the same CPU, another CPU in the same system, or even some external agent that uses DMA (Direct Memory Access).
If you want to guarantee that this inc
is atomic, use the lock
prefix:
lock inc counter
lock
guarantees that nobody can update counter
between the load and the store.
Regarding more complicated instructions, you usually can't assume that they'll execute atomically, unless they support the lock
prefix.
The answer is: it depends!
Here is some confusion around, what an assembler instruction is. Normally, one assembler instruction is translated into exactly one machine instruction. The excemption is when you use macros -- but you should be aware of that.
That said, the question boils down is one machine instruction atomic?
In the good old days, it was. But today, with complex CPUs, long running instructions, hyperthreading, ... it is not. Some CPUs guarantee that some increment/decrement instructions are atomic. The reason is, that they are neat for very simple syncronizing.
Also some CPU commands are not so problematic. When you have a simple fetch (of one piece of data that the processor can fetch in one piece) -- the fetch itself is of course atomic, because there is nothing to be divided at all. But when you have unaligned data, it becomes complicated again.
The answer is: It depends. Carefully read the machine instruction manual of the vendor. In doubt, it is not!
Edit: Oh, I saw it now, you also ask for ++counter. The statement "most likely to be translated" can not be trusted at all. This largely depends also on the compiler of course! It gets more difficult when the compiler is making different optimizations.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With