Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

is multiple assignments of atomic variables, an atomic operation?

Tags:

c++

atomic

consider I have two atomic booleans as follows.

private:
    std::atomic_bool x;
    std::atomic_bool y;

Can I say the following operation is atomic? or do I have to use lock_guard to to make sure they are assigned together?

x = y = true; // are two bools assigned together atomically?

also consider in another thread I want to read these booleans.

if(!x && !y) ...

my assumption is that this is not atomic, maybe its better to use atomic<int> instead?

like image 343
M.kazem Akhgary Avatar asked Jul 29 '17 12:07

M.kazem Akhgary


People also ask

Is assignment an atomic operation?

Yes. Assignment of premitive types and reference types are atomic. Read the ECMA C# language specification. Actually, that's not entirely true, only assignments to reference types, bool, char, byte, sbyte, short, ushort, uint, int and float are atomic.

What are atomic operations in OS?

Atomic operations are sequences of instructions that guarantee atomic accesses and updates of shared single word variables. This means that atomic operations cannot protect accesses to complex data structures in the way that locks can, but they provide a very efficient way of serializing access to a single word.

What are atomic operations in C?

Atomic operations are intended to allow access to shared data without extra protection (mutex, rwlock, …). This may improve: ● single thread performance ● scalability ● overall system performance.

Is += an atomic operation?

For the change in value to be visible across cores, a += (for instance) would have to load the value, add the increment and then store it. This means that the operation will not be atomic. To ensure atomicity you'd need to put appropriate locking around the operation.


2 Answers

No, it is not. All that an atomic operation guarantees is that no intervening operation happens on the variable. In your example it is entirely possible that y gets assigned, something unrelated happens (but only in another thread; in the current thread re-ordering will not happen because of the memory fence implied by operator= on an atomic), and then x gets assigned. The same is true when reading them.

If you really want these operations to be atomic, you'll need to use a single atomic type that encapsulates both pieces of information. There's many ways to do this; you can use a char and utilize different bits at the cost of some bit masking operations, you can use a 16 bit integer, but I will illustrate with the clearest (IMHO) approach: a struct with two booleans.

struct MyBools {
  bool x;
  bool y;
};

bool operator==(const MyBools& lhs, const MyBools& rhs) {
    return lhs.x == rhs.x && lhs.y == rhs.y;
}

using MyAtomicBools = std::atomic<MyBools>;

MyAtomicBools b{true, true};
...
if (b == MyBools{false, false}) { ... }

This may or may not optimize as well as using a 16 bit integer and casting out the two booleans by hand. gcc seems to optimize this extremely well; it turns the set operation into a single write + memory fence, but clang doesn't do as well: https://godbolt.org/g/moiT9Y.

like image 124
Nir Friedman Avatar answered Oct 10 '22 04:10

Nir Friedman


x = y = true; // are two bools assigned together atomically?

That line is clearly not an atomic operation as x and y are in two different locations in memory: it is impossible to set two locations that are not continuous³ one each other at the same time.

The atomic word implies that read or write is done in one cpu cycle¹, so one variable is safe, but x and y are two different atomic variables.

If you have any doubts about don't hesitate to look at the binary code produced, through the use of a disassembler.

if(!x && !y) ...

The same: the CPU have to access the value of the two different variables by copying the values into it own registers, make a boolean evaluation, negate, and perform an evaluation²; clearly not atomic operations.

¹ it surely is not so simple, but from a higher language developper point of view, you should think that
² again it is not so simple, as the compiler can make optimizations, and the CPU can make some things by itself
³ even with continuous locations, the total size must be readable/writable in one loop: 1Mo of data is clearly not readable in one loop by the cpu, even if all the data was continuously side by side.

like image 27
lemmel Avatar answered Oct 10 '22 04:10

lemmel