api
api

Reputation: 321

Does golang atomic.Load have a acquire semantics?

Given a c++ code snip:

int a = 0;
atomic<int> b{0};

Thread 1                         
a = 1;
b.store(1,memory_order_release);

Thread 2
while(!b.load(memory_order_acquire)); 
assert(a==1);

We know the assert never fire.

At the other hand, golang atomic.Store uses xchg instruction which implicts memory-barrier, so it can result in memory_order_release semantics as c++11.

//go:noescape
func Store(ptr *uint32, val uint32)
TEXT runtime∕internal∕atomic·Store(SB), NOSPLIT, $0-12
    MOVQ    ptr+0(FP), BX
    MOVL    val+8(FP), AX
    XCHGL   AX, 0(BX)
    RET

However, the implementation of atomic.Load is pure go code, which means just mov instruction when assembly.

//go:nosplit
//go:noinline
func Load(ptr *uint32) uint32 {
    return *ptr
}

So, does golang atomic.Load have a acquire semantics?
If do how it works, and if not how to insure memory-ordering or make a=1 visible?

Upvotes: 6

Views: 1560

Answers (1)

Eloff
Eloff

Reputation: 21688

On strongly ordered architectures like x86/amd64, acquire load and release store are just regular loads and stores. To make them atomic you need to ensure the memory is aligned to the operand size (automatic in Go), and that the compiler doesn't re-order them in incompatible ways, or optimize them away (e.g. reuse a value in a register instead of reading it from memory.)

The Go atomic Load* and Store* functions are sequentially consistent. This is a stronger form of memory ordering that requires memory fences (or instructions that have an implicit memory fence) even on x86/amd64.

Quoting rsc:

Go's atomics guarantee sequential consistency among the atomic variables (behave like C/C++'s seqconst atomics), and that you shouldn't mix atomic and non-atomic accesses for a given memory word.

Upvotes: 4

Related Questions