DEV Community

Understanding Atomics and Memory Ordering

kprotty on April 09, 2021

Atomics and Memory Ordering always feel like an unapproachable topic. In the sea of poor explanations, I wish to add another by describing how I re...
Collapse
 
jeikabu profile image
jeikabu • Edited

Pretty well-written, accessible article of a tricky subject. The exception being the last paragraph before the conclusion where you mention "full barrier" and "write-combined memory". I'm guessing that will baffle some people. But, it would have made the post much longer...

But great post overall! Synchronization is a fascinating subject that I feel is poorly understood and doesn't get enough attention. Further reading on lock-free data-structures, the ABA problem, dealing with hw/sw interrupts, spin-locks, priority inversion, and so on. See, I'm excited, is everyone else? 😁

Collapse
 
nacho00112 profile image
nacho00112

full barrier is what SeqCst does
write-combined memory instructions are multithreaded stores
at least that is what I understood, but I think I'm right

Collapse
 
nd profile image
nd

Trying to wrap my head around fences.

Shouldn't ordering be the following?

A fence(Release) makes previous non-Release atomic stores into Release if they have a matching Acquire atomic load or matching fence(Acquire).

A fence(Acquire) makes subsequent non-Acquire atomic loads into Acquire if they have a matching Release atomic store or matching fence(Release).

Collapse
 
kprotty profile image
kprotty

I don't think so: write(); fence(Release); store() under those rules would imply that the write() is Release when in-fact the store() might be the one that is used to observe the contents of the write(), meaning the store() would have to be Release.

"Promoting the store() to Release" might be the wrong imagery to use on my part. The C++ Atomics Reference describes it much more concretely:

A release fence F in thread A synchronizes-with atomic acquire operation Y in thread B, if:

  • there exists an atomic store X (with any memory order)
  • F is sequenced-before X in thread A
Collapse
 
darklynx profile image
Dark Lynx

Great article! Thanks!

But I would like to note one think that confused me a little.
In the example of reordering, the memory order was not mentioned.
If we talk about C++, which was mentioned at the beginning, the default order is seq_cst.
It means that atomic_store(&has_data, True, Release) and atomic_load(&has_data) operations will employ synchronized-with relationship and will guarantee that order of write and read.
So, strictly speaking, the example should always work and does not introduce any reordering around these atomics.

Please, correct me in case I am wrong.

Collapse
 
hardik_rajpal_0a4d546af48 profile image
Hardik Rajpal • Edited

Great article! I want to confirm that when you say

We then declare one more constraint: All memory operations before a given Release can be observed to happen-before a matching Acquire. You could think of it as changes from the Release becoming visible in a git push manner to the Acquire which does a sort of git pull. This solves the visibility problem.

  1. This constraint is for matching Release and Acquire operations in the same thread, right? Otherwise these constraints would be functioning as a synchronization primitive. Thanks again for the great article!
Collapse
 
gberthiaume profile image
G. Berthiaume

Thanks for writing this article.
It's a pleasure to see advanced topics on dev.to.