0

I've compiled and analyzed the assembly output for:

struct S{
public:
    int a,b,c,d,e,f,g,h,i,j,k;
};

int main() {
    S s;
    std::atomic<S> as;
    as.store(s);
    return 0;
}

I want to see how does is it implemented atomic store in fact. It is easy when it comes to aligned "small" operands. But, now we have a wider operand so it is a more complicated situation.

In my other question ( Atomicity on x86) @Peter Cordes said:

For wider operands, like atomically writing new data into multiple entries of a struct, you need to protect it with a lock which all accesses to it respect. (You may be able to use x86 lock cmpxchg16b with a retry loop to do an atomic 16b store. Note that there's no way to emulate it without a lock.)

Ok, but what does it mean exactly? What does it mean to lock? Especially, I know that lock is a prefix that ensures about atomicity of "prefixed" instruction. Especially, @Peter Cordes said:

You may be able to use x86 lock cmpxchg16b with a retry loop to do an atomic 16b store

I cannot understand how it is possible to keep it atomic? Ok, I can imagine that 16B chunk of memory can be stored in atomic way? But what about next iterations?

I hope that my doubts are understandable because I had a problem to express it.


I was debugging above program and, on my eye, the magic is behind atomic_store. I suppose that this function executes what @Peter Cordes said. If someone wants, I can paste here disassembled __atomic_store

Community
  • 1
  • 1
Gilgamesz
  • 4,727
  • 3
  • 28
  • 63
  • 4
    You said that you have analyzed the generated machine code. Well, then *you* can tell *us* how this is implemented! – Kerrek SB Jul 21 '16 at 22:32
  • Yes, go ahead and paste in the disassembly for __atomic_store. gcc just generates a call to it with the data to be stored passed by reference. – Peter Cordes Jul 22 '16 at 00:03
  • Am I missing the question here?? – David Hoelzer Jul 23 '16 at 10:33
  • @DavidHoelzer: No, you're not missing anything, there isn't really a question here. My "answer" started out as a comment, but then I realized it did actually answer some of what the question was asking, so I went ahead and made it an answer. The whole thing should probably have been a comment/reply on my answer to the previous question. – Peter Cordes Jul 23 '16 at 11:14

1 Answers1

3

You may be able to use x86 lock cmpxchg16b with a retry loop to do an atomic 16B store

Did I really say 16b instead of 16B? oops. I'll fix that as part of a larger edit.

That lets you do one 16B atomic store, but do it as a read-modify-rewrite that keeps retrying until the compare part succeeds. You can't use this to store more than 16B atomically.


What does it mean to lock? Especially, I know that lock is a prefix that ensures about atomicity of "prefixed" instruction.

Lock as in spinlock / mutex, not lock prefix. The lock prefix only ever works on read-modify-write instructions; there is no lock mov [mem], eax to do an atomic unaligned store or something. locked bus cycles are always read-modify-write, as documented by Intel in the docs for cmpxchg. So a lock mov store would also generate a load, which has different semantics if you use it on memory-mapped I/O. (A read can trigger side effects).


I've compiled and analyzed the assembly output for ...

Why would you put that code in main(), and store uninitialized garbage from s into as? Besides that, main is special in a few ways. Always better to just write a function that takes an arg (or just affects a global). And the atomic<s> needs to be global, not a local that can potentially be partly optimized away, if you want to be sure you're seeing what gcc "really" does.

#include <atomic>
struct S{ int a,b,c,d,e,f,g,h,i,j,k;  };  // or int a[11]
std::atomic<S> as;
void atomic_struct_store_zero() {
    S s = { 0 };     // initializes all members to zero
    as.store(s);
}

This compiles to a function call to __atomic_store, passing src and dest pointers and a size. Presumably it uses a lock somewhere, but the lock isn't part of as. (sizeof(as) == sizeof(S) == 44).

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847