{"id":4823,"date":"2016-07-19T09:30:30","date_gmt":"2016-07-19T09:30:30","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/fences-as-memory-barriers\/"},"modified":"2023-06-26T12:49:32","modified_gmt":"2023-06-26T12:49:32","slug":"fences-as-memory-barriers","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/fences-as-memory-barriers\/","title":{"rendered":"Fences are Memory Barriers"},"content":{"rendered":"<p>The key idea of a <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence<\/span> is to establish synchronization and ordering constraints between threads without an atomic operation.<\/p>\n<p><!--more--><\/p>\n<p><span style=\"font-family: courier new,courier;\">std::atomic_thread_fence<\/span> are called fences or memory barriers. So you immediately get the idea of what a<span style=\"font-family: courier new,courier;\"> std::atomic_thread_fence<\/span> is all about.<\/p>\n<p>A <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence<\/span> prevents specific operations can overcome a memory barrier.<\/p>\n<h2>Memory barriers<\/h2>\n<p>But what does that mean? Specific operations which can not overcome a memory barrier. What kind of operations? From a bird&#8217;s perspective, we have two operations: Read and write or load and store. So the expression <span style=\"font-family: courier new,courier;\">if(resultRead) return result<\/span> is a load, followed by a store operation.<\/p>\n<p>There are four different ways to combine load and store operations:<\/p>\n<ul>\n<li><strong>LoadLoad<\/strong>: A load followed by a load.<span style=\"font-family: courier new,courier;\"> <br \/><\/span><\/li>\n<li><strong>LoadStore:<\/strong> A load followed by a store.<\/li>\n<li><strong>StoreLoad:<\/strong> A store followed by a load.<\/li>\n<li><strong>StoreStore:<\/strong> A store followed by a store.<\/li>\n<\/ul>\n<p>Of course, more complex operations consist of a load and store part <span style=\"font-family: courier new,courier;\">(count++). <\/span>But these operations didn&#8217;t contradict my general classification.<\/p>\n<p>But what about memory barriers? If you place memory barriers between two operations like LoadLoad, LoadStore, StoreLoad, or StoreStore, you have the guarantee that specific LoadLoad, LoadStore, StoreLoad, or StoreStore operations can not be reordered. The risk of reordering is always given if non-atomics or atomics with relaxed semantics are used.&nbsp;<\/p>\n<p>Typically, three kinds of memory barriers are used. They are called <strong>a full fence, acquire fence<\/strong> and <strong>release fence.<\/strong> Only to remind you. Acquire is a load; release is a store operation. So, what happens if I place one of the three memory barriers between the four load and store operations combinations?<\/p>\n<ul>\n<li><strong>Full fence: <\/strong>A full fence <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence()<\/span> between two arbitrary operations prevents the reordering of these operations. But that guarantee will not hold for StoreLoad operations. They can be reordered.<\/li>\n<li><strong>Acquire fence: <\/strong>An acquire fence <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence(std::memory_order_acquire<\/span>) prevents a read operation before an acquire fence can be reordered with a reading or write operation after the acquire fence.<em><\/em><\/li>\n<li><strong>Release fence: <\/strong>A <em>release fence<\/em> <span style=\"font-family: courier new,courier;\">std::memory_thread_fence(std::memory_order_release)<\/span> prevents a read or write operation before a release fence can be reordered with a write operation after a release fence.<em><\/em><\/li>\n<\/ul>\n<p>I admit that I invested&nbsp;a lot of energy to get the definitions of an to acquire and release fence and their consequences for lock-free programming. Especially the subtle difference to the acquire-release semantics of atomic operations are not so easy to get. But, before I come to that point, I will illustrate the definitions with graphics.<\/p>\n<\/p>\n<h3>Memory barriers illustrated<\/h3>\n<p>Which kind of operations can overcome a memory barrier? Have a look at the following three graphics. If the arrow is crossed with a red barn, the fence prevents this operation.<\/p>\n<h4>Full fence<\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4819\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/FullFence.png\" alt=\"FullFence\" width=\"600\" height=\"163\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/FullFence.png 914w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/FullFence-300x82.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/FullFence-768x209.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>Of course, you can explicitly write instead of<span style=\"font-family: courier new,courier;\"> std::atomic_thread_fence()<\/span> <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence(std::memory_order_seq_cst)<\/span>. Per default, <a href=\"https:\/\/www.modernescpp.com\/index.php\/sequential-consistency\"> sequential consistency <\/a>is used for fences. Is sequential consistency used for a full fence, the <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence<\/span> follows a global order.<\/p>\n<h4>Acquire fence<\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4820\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/AcquireFence.png\" alt=\"AcquireFence\" width=\"600\" height=\"174\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/AcquireFence.png 905w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/AcquireFence-300x87.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/AcquireFence-768x222.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<h4>Release fence<\/h4>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4821\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/ReleaseFence.png\" alt=\"ReleaseFence\" width=\"600\" height=\"172\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/ReleaseFence.png 908w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/ReleaseFence-300x86.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/ReleaseFence-768x221.png 768w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/p>\n<p>But I can depict the three memory barriers even more concisely.<\/p>\n<h3>Memory barriers at a glance<\/h3>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4822\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/loadStore.png\" alt=\"loadStore\" width=\"450\" height=\"332\" style=\"margin: 15px;\" \/><\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>That was the theory. The practice will follow in the <a href=\"https:\/\/www.modernescpp.com\/index.php\/acquire-release-fences\">next post<\/a>. In this post, I compare the first step, an acquire fence with an acquires operation, a release fence with a release operation. In the second step, I port a producer-consumer scenario with acquire release operations to fences.&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The key idea of a std::atomic_thread_fence is to establish synchronization and ordering constraints between threads without an atomic operation.<\/p>\n","protected":false},"author":21,"featured_media":4819,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[368],"tags":[505,522,434,504,519],"class_list":["post-4823","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-memory-model","tag-acquire-release-semantic","tag-atomic_thread_fence","tag-atomics","tag-relaxed-semantics","tag-sequential-consistency"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4823","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/users\/21"}],"replies":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/comments?post=4823"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4823\/revisions"}],"predecessor-version":[{"id":6963,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4823\/revisions\/6963"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/4819"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=4823"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=4823"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=4823"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}