{"id":4829,"date":"2016-07-21T15:59:42","date_gmt":"2016-07-21T15:59:42","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/acquire-release-fences\/"},"modified":"2023-06-26T12:49:09","modified_gmt":"2023-06-26T12:49:09","slug":"acquire-release-fences","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/acquire-release-fences\/","title":{"rendered":"Acquire-Release Fences"},"content":{"rendered":"<p>Acquire and release fences guarantee similar <a href=\"https:\/\/www.modernescpp.com\/index.php\/synchronization-and-ordering-constraints\">synchronization and ordering constraints<\/a> as atomics with <a href=\"https:\/\/www.modernescpp.com\/index.php\/acquire-release-semantic\">acquire-release <\/a>semantics. Similar because the differences are in the details.<\/p>\n<p><!--more--><\/p>\n<p>The most apparent difference between acquire and release memory barriers (fences) and atomics with acquire-release semantics is that memory barriers need no operations on atomics. But there is a more subtle difference. The acquire and release memory barriers are more heavyweight.<\/p>\n<h2>Atomic operations versus memory barriers<\/h2>\n<p>To simplify my writing job, I will now speak of acquire operations if I use memory barriers or atomic operations with acquire semantics. The same will hold for release operations.<\/p>\n<p>The key idea of an acquire and a release operation is that it establishes synchronizations and ordering constraints between threads. This will also hold for atomic operations with relaxed semantic or non-atomic operations. So you see, the acquire and release operations come in pairs. In addition, the operations on atomic variables with acquire-release semantic must hold that these act on the same atomic variable. Said that I&nbsp;will, in the first step, look at these operations in isolation.<\/p>\n<p>I start with the acquire operation.<\/p>\n<\/p>\n<h3>Acquire operation<\/h3>\n<p>A read operation on an atomic variable attached with <span style=\"font-family: courier new,courier;\">std::memory_order_acquire<\/span> is an acquire operation.<\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4824\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireOperation.png\" alt=\"acquireOperation\" width=\"400\" height=\"80\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireOperation.png 637w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireOperation-300x60.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/>&nbsp;<\/p>\n<p>In opposite to that, there is the <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence<\/span> with acquire semantics.<\/p>\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=\"500\" height=\"118\" style=\"margin: 15px;\" \/><\/p>\n<p>This comparison emphasises two points.<\/p>\n<ol>\n<li>A memory barrier with acquire semantics establishes stronger ordering constraints. Although the acquire operation on an atomic and a memory barrier requires that no read or write operation can be moved before the acquire operation, there is an additional guarantee with the acquire memory barrier. No read operation can be moved after the acquire memory barrier.<\/li>\n<li>The relaxed semantic is sufficient for the reading of the atomic variable <span style=\"font-family: courier new,courier;\">var<\/span>. The <span style=\"font-family: courier new,courier;\">std::atomc_thread_fence(std::memory_order_acquire)<\/span> ensures that this operation can not be moved after the acquire fence.<\/li>\n<\/ol>\n<p>The similar statement holds for the release memory barrier.<\/p>\n<h3>Release operation<\/h3>\n<p>The write operation on an atomic variable attached to the memory model std::memory_order_release is a release operation.<\/p>\n<p>&nbsp;<img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4825\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/releaseOperation.png\" alt=\"releaseOperation\" width=\"400\" height=\"105\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/releaseOperation.png 588w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/releaseOperation-300x79.png 300w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>And further the release memory barrier.<\/p>\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=\"500\" height=\"140\" style=\"margin: 15px;\" \/><\/p>\n<p>In addition to the release operation on an atomic variable <span style=\"font-family: courier new,courier;\">var,<\/span> the release barrier guarantees two points:<span style=\"font-family: courier new,courier;\"><\/span><\/p>\n<ol>\n<li>Store operations can&#8217;t be moved before the memory barrier.<\/li>\n<li>It&#8217;s sufficient for the variable <span style=\"font-family: courier new,courier;\">var<\/span> to have relaxed semantics.<span style=\"font-family: courier new,courier;\"><\/span><\/li>\n<\/ol>\n<p>If you want a simple overview of memory barriers, please read the last post in this blog. But now, I want to go one step further and build a program out of the presented components.<\/p>\n<h2>Synchronization with atomic operations versus memory barriers<\/h2>\n<p>I implement it as a starting point for comparing a typical consumer-producer workflow with acquire-release semantics. I will do this job with atomics and memory barriers.<\/p>\n<p>Let&#8217;s start with atomics because most of us are&nbsp;comfortable with them. That will not hold for memory barriers. They are almost completely ignored in the literature on the C++ memory model.<\/p>\n<h3>Atomic operations<\/h3>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ acquireRelease.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;thread&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;string&gt;<\/span>\r\n \r\nstd::atomic&lt;std::string*&gt; ptr;\r\n<span style=\"color: #2b91af;\">int<\/span> data;\r\nstd::atomic&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; atoData;\r\n \r\n<span style=\"color: #2b91af;\">void<\/span> producer(){\r\n    std::string* p  = <span style=\"color: #0000ff;\">new<\/span> std::string(<span style=\"color: #a31515;\">\"C++11\"<\/span>);\r\n    data = 2011;\r\n    atoData.store(2014,std::memory_order_relaxed);\r\n    ptr.store(p, std::memory_order_release);\r\n}\r\n \r\n<span style=\"color: #2b91af;\">void<\/span> consumer(){\r\n    std::string* p2;\r\n    <span style=\"color: #0000ff;\">while<\/span> (!(p2 = ptr.load(std::memory_order_acquire)));\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"*p2: \"<\/span> &lt;&lt; *p2 &lt;&lt; std::endl;\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"data: \"<\/span> &lt;&lt; data &lt;&lt; std::endl;\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"atoData: \"<\/span> &lt;&lt; atoData.load(std::memory_order_relaxed) &lt;&lt; std::endl;\r\n}\r\n \r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n    \r\n    std::cout &lt;&lt; std::endl;\r\n    \r\n    std::<span style=\"color: #0000ff;\">thread<\/span> t1(producer);\r\n    std::<span style=\"color: #0000ff;\">thread<\/span> t2(consumer);\r\n    \r\n    t1.join();\r\n    t2.join();\r\n    \r\n    <span style=\"color: #0000ff;\">delete<\/span> ptr;\r\n    \r\n    std::cout &lt;&lt; std::endl;\r\n    \r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I hope this program looks familiar to you. That is the classic that I used in the post to<a href=\"https:\/\/www.modernescpp.com\/index.php\/tag\/memory-order-consume\"> memory_order_consume<\/a>. The graphic directly explains why the consumer thread t2 sees all values from the producer thread t1.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4826\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireRelease.png\" alt=\"acquireRelease\" width=\"700\" height=\"337\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireRelease.png 865w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireRelease-300x144.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireRelease-768x369.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>The program is well-defined, because the <em>happens-before<\/em> relation is transitive. I have only to combine the three <span style=\"font-family: courier new,courier;\">happens-before<\/span> relations:<\/p>\n<ol>\n<li>Line 13 &#8211; 15 <em>happens-before<\/em>&nbsp;line 16<span style=\"font-family: courier new,courier;\"> (ptr.store(p,std::memory_order_release)<\/span>.<\/li>\n<li>Line 21<span style=\"font-family: courier new,courier;\"> while(!(p2= ptrl.load(std::memory_order_acquire)))<\/span><em> happens-before <\/em>lines 22 &#8211; 24.<\/li>\n<li>Line 16 <em>synchronizes-with<\/em> line 21.<strong><span style=\"color: #ff0000;\"> =&gt;<\/span><\/strong> Line 16<em> happens-before<\/em> line 21.<\/li>\n<\/ol>\n<p>&nbsp;<\/p>\n<p>But now the story gets more thrilling. How can I adjust the workflow to memory barriers?<\/p>\n<h3>Memory barriers<\/h3>\n<p>It&#8217;s straightforward to port the program to memory barriers.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<table>\n<tbody>\n<tr>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> 1\r\n 2\r\n 3\r\n 4\r\n 5\r\n 6\r\n 7\r\n 8\r\n 9\r\n10\r\n11\r\n12\r\n13\r\n14\r\n15\r\n16\r\n17\r\n18\r\n19\r\n20\r\n21\r\n22\r\n23\r\n24\r\n25\r\n26\r\n27\r\n28\r\n29\r\n30\r\n31\r\n32\r\n33\r\n34\r\n35\r\n36\r\n37\r\n38\r\n39\r\n40\r\n41\r\n42\r\n43<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ acquireReleaseFences.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;thread&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;string&gt;<\/span>\r\n \r\nstd::atomic&lt;std::string*&gt; ptr;\r\n<span style=\"color: #2b91af;\">int<\/span> data;\r\nstd::atomic&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; atoData;\r\n \r\n<span style=\"color: #2b91af;\">void<\/span> producer(){\r\n    std::string* p  = <span style=\"color: #0000ff;\">new<\/span> std::string(<span style=\"color: #a31515;\">\"C++11\"<\/span>);\r\n    data = 2011;\r\n    atoData.store(2014,std::memory_order_relaxed);\r\n    std::atomic_thread_fence(std::memory_order_release);\r\n    ptr.store(p, std::memory_order_relaxed);\r\n}\r\n \r\n<span style=\"color: #2b91af;\">void<\/span> consumer(){\r\n    std::string* p2;\r\n    <span style=\"color: #0000ff;\">while<\/span> (!(p2 = ptr.load(std::memory_order_relaxed)));\r\n    std::atomic_thread_fence(std::memory_order_acquire);\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"*p2: \"<\/span> &lt;&lt; *p2 &lt;&lt; std::endl;\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"data: \"<\/span> &lt;&lt; data &lt;&lt; std::endl;\r\n    std::cout &lt;&lt; <span style=\"color: #a31515;\">\"atoData: \"<\/span> &lt;&lt; atoData.load(std::memory_order_relaxed) &lt;&lt; std::endl;\r\n}\r\n \r\n<span style=\"color: #2b91af;\">int<\/span> main(){\r\n    \r\n    std::cout &lt;&lt; std::endl;\r\n    \r\n    std::<span style=\"color: #0000ff;\">thread<\/span> t1(producer);\r\n    std::<span style=\"color: #0000ff;\">thread<\/span> t2(consumer);\r\n    \r\n    t1.join();\r\n    t2.join();\r\n    \r\n    <span style=\"color: #0000ff;\">delete<\/span> ptr;\r\n    \r\n    std::cout &lt;&lt; std::endl;\r\n    \r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The first step is to insert just in place of the operations with acquire and release semantic the corresponding memory barriers with acquire and release semantics (lines 16 and 23). In the next step, I change the atomic operations with acquire or release semantics to relaxed semantics (lines 17 and 22). That was already mechanical. Of course, I can only replace one acquire or release operation with the corresponding memory barrier. The key point is that the release operation establishes with the acquire operation a <em>synchronize-with<\/em> relation and, therefore, a <em>happens-before<\/em> relation.<\/p>\n<p>For the more visual reader, the whole description in&nbsp;a picture.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4827\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseFences.png\" alt=\"acquireReleaseFences\" width=\"700\" height=\"321\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseFences.png 868w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseFences-300x138.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseFences-768x352.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>The key question is. Why do the operations after the acquire memory barrier sees the effects of the operations before the release memory barrier? Because <span style=\"font-family: courier new,courier;\">data<\/span> is a non-atomic variable and <span style=\"font-family: courier new,courier;\">atoData<\/span> is used with relaxed semantics, both can be reordered. But that&#8217;s not possible. The <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence(std::memory_order_release)<\/span> as a release operation in combination with the <span style=\"font-family: courier new,courier;\">std::atomic_thread_fence(std::memory_order_acquire)<\/span> forbids the partial reordering. To follow my reasoning in detail, read the analysis of the memory barriers at the beginning of the post.<\/p>\n<p>For clarity, the whole reasoning is to the point.<\/p>\n<ol>\n<li>The acquire and release memory barriers prevent the reordering of the atomic and non-atomic operations across the memory barriers.<\/li>\n<li>The consumer thread <span style=\"font-family: courier new,courier;\">t2<\/span> is waiting in the <span style=\"font-family: courier new,courier;\">while (!(p2= ptr.load(std::memory_order_relaxed)))<\/span> loop until the pointer&nbsp;<span style=\"font-family: courier new,courier;\">ptr.stor(p,std::memory_order_relaxed) <\/span>is set in the producer thread t1.<span style=\"font-family: courier new,courier;\"><br \/><\/span><\/li>\n<li>The release memory barrier<em> synchronizes-with<\/em> the acquire memory barrier.<\/li>\n<\/ol>\n<p>Finally, the output of the programs.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4828\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseAcquireReleaseFences.png\" alt=\"acquireReleaseAcquireReleaseFences\" style=\"margin: 15px;\" width=\"517\" height=\"307\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseAcquireReleaseFences.png 517w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseAcquireReleaseFences-300x178.png 300w\" sizes=\"auto, (max-width: 517px) 100vw, 517px\" \/><\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>But now, to the weakest memory model. The relaxed semantics will be the topic of the <a href=\"https:\/\/www.modernescpp.com\/index.php\/relaxed-semantic\">next post<\/a>. There are no ordering constraints.&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Acquire and release fences guarantee similar synchronization and ordering constraints as atomics with acquire-release semantics. Similar because the differences are in the details.<\/p>\n","protected":false},"author":21,"featured_media":4824,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[368],"tags":[505,522,434],"class_list":["post-4829","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"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4829","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=4829"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4829\/revisions"}],"predecessor-version":[{"id":6962,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4829\/revisions\/6962"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/4824"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=4829"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=4829"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=4829"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}