Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does one assembler instruction always execute atomically? [duplicate]

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?

like image 264
vava Avatar asked Jul 07 '09 08:07

vava


People also ask

What is meant by instructions being executed atomically?

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.

Are all CPU instructions Atomic?

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.

Why it is not allowed to have two operands from memory in an instruction?

It would have added complexity in decoding and had probably required one more ALU unit.

How does Assembly Stack work?

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.


2 Answers

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:

  • load counter to a hidden register on the CPU
  • increment the register
  • store the updated register in counter

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.

like image 143
Nathan Fellman Avatar answered Oct 18 '22 22:10

Nathan Fellman


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.

like image 45
Juergen Avatar answered Oct 18 '22 20:10

Juergen