0

In Concurrency In Action, it is stated:

All operations on an atomic type are defined as atomic, and assignment and copy-construction involve two objects. A single operation on two distinct objects can’t be atomic.

But I'm a little confused

atomic<int> i1, i2;
i1 = 0;
i2 = 1;
i1 = i2;          // error
i1.store(i2);  // but it works, is this operation atomic?

The last line of the code completes the assignment, it also involved two objects

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
x f
  • 63
  • 4
  • "concurrencyInAction" is a book? When it says "atomic" does it refer to `std::atomic` ? – 463035818_is_not_an_ai Sep 09 '22 at 08:36
  • 7
    `i1.store(i2);` this is a atomic load followed by an atomic store. The `load and store` is not atomic but the 2 operations are individually atomic. This is why `atomic& operator=( const atomic& ) = delete;` is deleted https://en.cppreference.com/w/cpp/atomic/atomic/operator%3D So as to make clear that assignment of atomics is __not__ a single atomic operation but two independent atomic operations. – Richard Critten Sep 09 '22 at 08:37
  • 2
    A few machines (like early m68k) could do atomic operations on two separate memory locations at once, using DCAS. Most hardware doesn't support this, and std::atomic doesn't expose the functionality because that would require `atomic` to be non-lock-free on 99% of the machines people care about. – Peter Cordes Sep 10 '22 at 04:27
  • @PeterCordes Hi, I have one last question:atomic at; int i = 10; is (at = i;) operation atomic? Because you have to read i variable, then assign to at. It's two instructions – x f Sep 11 '22 at 02:29
  • 1
    The assignment to `at` is an atomic store; readers of `at` will either see this store or they won't, but they're guaranteed not to see some bytes from an old value and some bytes from this store. It's all or nothing, which is what atomicity is all about for a single operation, especially a pure-store or pure-load (see [Atomicity on x86](https://stackoverflow.com/a/38465341)). The read of `i` is not atomic because `int i` isn't an atomic object. So there's data race UB if another thread is writing `i` at the same time. And regardless, it could at best be a separately-atomic load. – Peter Cordes Sep 11 '22 at 02:35

1 Answers1

4
atomic<int> i1, i2;
i1 = 0; // this is calling atomic<int>& atomic<int>::operator=(int)
i2 = 1; // this is also calling atomic<int>& atomic<int>::operator=(int)
i1 = i2; // this is also calling 
// atomic<int>& atomic<int>::operator=(constatomic<int>&)
// (so called copy assignement operator which is explicitly deleted 
// https://en.cppreference.com/w/cpp/atomic/atomic/operator%3D)

// this is completely different beast
i1.store(i2);
// it actually does two things:
int temporary = i2.load(); // *1
i2.store(temporary); // *2
// what happens here is:
// -> i2 get implicitly converted to int (*1)
// -> i1.store gets called with that int (*2)

// each of those 2 instructions are atomic but not both of them at once
// in other words they i1.store(i2) is not atomic as a whole
MichalK
  • 126
  • 3
  • 2
    _"...// (so called copy constructor which is explicitly deleted ..."_ assignment operator – Richard Critten Sep 09 '22 at 08:42
  • The explanations were very detailed, but if type is atomic (Test is a user defined class), Test t; atomic at; at = t; How does this ensure that the assignment is atomic – x f Sep 09 '22 at 09:02
  • 1
    Shortly - it doesn't (actually it does but is not a good usage of atomic https://en.cppreference.com/w/cpp/atomic/atomic/is_lock_free). atomic is a wrapper for CPU instructions are said to be atomic (https://en.cppreference.com/w/cpp/thread under atomic operations). Assuming you would use std::atomic it will use internally std::mutex to ensure atomicity of operations. So if you want to ensure that at = t is atomic it is better to use std::mutex with unique_lock/scoped_lock – MichalK Sep 09 '22 at 12:25
  • 1
    `atomic` can be lock-free if it's small enough, like `struct { char c[8]; }`. Or two `int32_t` members, or whatever. Or even 16 bytes on some systems. – Peter Cordes Sep 10 '22 at 04:24
  • one more question: atomic at; int i = 10; is (at = i;) operation atomic? Because you have to read i variable, than assign to at. It's two instructions – x f Sep 10 '22 at 08:56