{"id":4818,"date":"2016-07-16T09:48:02","date_gmt":"2016-07-16T09:48:02","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/acquire-release-semantic-the-typical-misunderstanding\/"},"modified":"2023-06-26T12:50:00","modified_gmt":"2023-06-26T12:50:00","slug":"acquire-release-semantic-the-typical-misunderstanding","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/acquire-release-semantic-the-typical-misunderstanding\/","title":{"rendered":"Acquire-Release Semantics &#8211; The Typical Misunderstanding"},"content":{"rendered":"<p>A release operation synchronizes-with an acquire operation on the same atomic variable. So we can easily synchronise threads, <span style=\"color: #ff0000;\"><strong>if<\/strong><\/span> &#8230; . Today&#8217;s post is about the <span style=\"color: #ff0000;\"><strong>if<\/strong>.<\/span><\/p>\n<p><!--more--><\/p>\n<p>What&#8217;s my motivation for writing a post about the typical misunderstanding of the acquire-release semantic? Sure, I and many of my listeners and trainees have already fallen into the trap. But at first the straightforward case.<\/p>\n<h2>Waiting included<\/h2>\n<p><span style=\"font-family: courier new,courier;\"><\/span>I use this simple program as a starting point.<span style=\"font-family: courier new,courier;\"><br \/><\/span><\/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<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ acquireReleaseWithWaiting.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;thread&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;vector&gt;<\/span>\r\n\r\nstd::vector&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; mySharedWork;\r\nstd::atomic&lt;<span style=\"color: #2b91af;\">bool<\/span>&gt; dataProduced(false);\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> dataProducer(){\r\n    mySharedWork={1,0,3};\r\n    dataProduced.store(true, std::memory_order_release);\r\n}\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> dataConsumer(){\r\n    <span style=\"color: #0000ff;\">while<\/span>( !dataProduced.load(std::memory_order_acquire) );\r\n    mySharedWork[1]= 2;\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(dataConsumer);\r\n  std::<span style=\"color: #0000ff;\">thread<\/span> t2(dataProducer);\r\n\r\n  t1.join();\r\n  t2.join();\r\n  \r\n  <span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #0000ff;\">auto<\/span> v: mySharedWork){\r\n      std::cout &lt;&lt; v &lt;&lt; <span style=\"color: #a31515;\">\" \"<\/span>;\r\n  }\r\n      \r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n  \r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The consumer thread t1 in line 17 waits until the consumer thread t2 in line 13 has set <span style=\"font-family: courier new,courier;\">dataProduced<\/span> to <span style=\"font-family: courier new,courier;\">true.dataPruduced <\/span>is the guard because it guarantees access to the non-atomic variable <span style=\"font-family: courier new,courier;\">mySharedWork<\/span> is synchronized. That means, at first, the producer thread t2 initializes <span style=\"font-family: courier new,courier;\">mySharedWork,<\/span> then the consumer thread t2 finishes the work by setting <span style=\"font-family: courier new,courier;\">mySharedWork[1]<\/span> to 2. So the program is well-defined.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4813\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithWaiting.png\" alt=\"acquireReleaseWithWaiting\" width=\"469\" height=\"184\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithWaiting.png 469w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithWaiting-300x118.png 300w\" sizes=\"auto, (max-width: 469px) 100vw, 469px\" \/><\/p>\n<p>The graphic shows the <em>happens-before<\/em> relation within the threads and the <em>synchronized-with<\/em> relation between the threads. <em>synchronize-with<\/em> establishes a <em>happens-before<\/em> relation. The rest of the reasoning is the transitivity of the&nbsp;<em>happens-before<\/em> relation. <span style=\"font-family: courier new,courier;\">mySharedWork={1,0,3} <em>happens-before<\/em> mySharedWork[1]=<\/span> 2.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4814\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withWaiting.png\" alt=\"withWaiting\" width=\"700\" height=\"312\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withWaiting.png 1191w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withWaiting-300x134.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withWaiting-1024x457.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withWaiting-768x343.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<p>But what aspect is often missing in this reasoning. The <strong><span style=\"color: #ff0000;\">if.<\/span><\/strong><\/p>\n<\/p>\n<h2>If, &#8230;<\/h2>\n<p>What is happing <strong><span style=\"color: #ff0000;\">if<\/span> <\/strong>the consumer thread t2 in line 17 is not waiting for the producer thread?<\/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<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #008000;\">\/\/ acquireReleaseWithoutWaiting.cpp<\/span>\r\n\r\n<span style=\"color: #0000ff;\">#include &lt;atomic&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;thread&gt;<\/span>\r\n<span style=\"color: #0000ff;\">#include &lt;vector&gt;<\/span>\r\n\r\nstd::vector&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; mySharedWork;\r\nstd::atomic&lt;<span style=\"color: #2b91af;\">bool<\/span>&gt; dataProduced(false);\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> dataProducer(){\r\n    mySharedWork={1,0,3};\r\n    dataProduced.store(true, std::memory_order_release);\r\n}\r\n\r\n<span style=\"color: #2b91af;\">void<\/span> dataConsumer(){\r\n    dataProduced.load(std::memory_order_acquire);\r\n    mySharedWork[1]= 2;\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(dataConsumer);\r\n  std::<span style=\"color: #0000ff;\">thread<\/span> t2(dataProducer);\r\n\r\n  t1.join();\r\n  t2.join();\r\n  \r\n  <span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #0000ff;\">auto<\/span> v: mySharedWork){\r\n      std::cout &lt;&lt; v &lt;&lt; <span style=\"color: #a31515;\">\" \"<\/span>;\r\n  }\r\n      \r\n  std::cout &lt;&lt; <span style=\"color: #a31515;\">\"\\n\\n\"<\/span>;\r\n  \r\n}\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The program has undefined behavior because there is a <a href=\"https:\/\/www.modernescpp.com\/index.php\/threads-sharing-data\">data race <\/a>on the variable <span style=\"font-family: courier new,courier;\">mySharedWork. <\/span>In case I let the program run, the undefined behavior gets immediately visible. That holds for Linux and Windows. <span style=\"font-family: courier new,courier;\"> <\/span><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4815\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaiting.png\" alt=\"acquireReleaseWithoutWaiting\" width=\"400\" height=\"429\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaiting.png 469w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaiting-280x300.png 280w\" sizes=\"auto, (max-width: 400px) 100vw, 400px\" \/><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4816\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaitingWin.PNG\" alt=\"acquireReleaseWithoutWaitingWin\" width=\"366\" height=\"206\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaitingWin.PNG 366w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/acquireReleaseWithoutWaitingWin-300x169.png 300w\" sizes=\"auto, (max-width: 366px) 100vw, 366px\" \/><\/p>\n<p>What&#8217;s the issue? It holds: <span style=\"font-family: courier new,courier;\">store(true, std::memory_order_release)<\/span> <em>synchron<\/em>izes<em>-with<\/em> <span style=\"font-family: courier new,courier;\">dataProduced.load(std::memory_order_acquire).<\/span> Yes, of course, but that doesn&#8217;t mean the acquire operation is waiting for the release operation. Exactly that is displayed in the graphic. In the graphic the&nbsp;<span style=\"font-family: courier new,courier;\"><span style=\"font-family: courier new,courier;\">d<\/span>ataProduced.load(std::memory_order_acquire)<\/span> instruction is performed before the instruction <span style=\"font-family: courier new,courier;\">dataProduced.store(true, std::memory_order_release). <\/span>So we have no<span style=\"font-family: courier new,courier;\"> <em>synchronize-with<\/em> <\/span>relation.&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-4817\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withoutWaiting.png\" alt=\"withoutWaiting\" width=\"700\" height=\"321\" style=\"margin: 15px;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withoutWaiting.png 1110w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withoutWaiting-300x138.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withoutWaiting-1024x470.png 1024w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2016\/07\/withoutWaiting-768x352.png 768w\" sizes=\"auto, (max-width: 700px) 100vw, 700px\" \/><\/p>\n<h3>The solution<\/h3>\n<p>synchronize-with means in this specific case: <strong><span style=\"color: #ff0000;\">If<\/span><\/strong> <span style=\"font-family: courier new,courier;\">dataProduced.store(true, std::memory_order_release)<\/span> happens before <span style=\"font-family: courier new,courier;\">dataProduced.load(std::memory_order_acquire)<\/span>, <strong><span style=\"color: #ff0000;\">then<\/span><\/strong> all visible effects of operations before <span style=\"font-family: courier new,courier;\">dataProduced.store(true, std::memory_order_release)<\/span> are visible after <span style=\"font-family: courier new,courier;\">dataProduced.load(std::memory_order_acquire). <\/span><span style=\"font-family: courier new,courier;\">The key is the word <strong><span style=\"color: #ff0000;\">if.<\/span><\/strong> <\/span>Exactly that <strong><span style=\"color: #ff0000;\">if<\/span> <\/strong>will be guaranteed in the first program with <span style=\"font-family: courier new,courier;\">(<span style=\"font-family: courier new,courier;\">while(!dataProduced.load(std::memory_order_acquire)<\/span>).<\/span><\/p>\n<p>Once again, but formal.<em><br \/><\/em><\/p>\n<ul>\n<li>All operations before <span style=\"font-family: courier new,courier;\">dataProduced.store(true, std::memory_order_release)<\/span><em>happens-before<\/em> all operations after <span style=\"font-family: courier new,courier;\">dataProduced.load(std::memory_order_acquire<\/span>), if holds: <span style=\"font-family: courier new,courier;\">dataProduced.store(true, std::memory_order_release)<\/span> <em>happens-before<\/em><span style=\"font-family: courier new,courier;\">&nbsp;dataProduced.load(std::memory_order_acquire).<\/span><\/li>\n<\/ul>\n<h2>What&#8217;s next?<\/h2>\n<p>Acquire-release semantic with operations on atomic variables. Does this work? Yeah, with fences. Have a look at the<a href=\"https:\/\/www.modernescpp.com\/index.php\/fences-as-memory-barriers\"> next post.<\/a><\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>A release operation synchronizes-with an acquire operation on the same atomic variable. So we can easily synchronise threads, if &#8230; . Today&#8217;s post is about the if.<\/p>\n","protected":false},"author":21,"featured_media":4813,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[368],"tags":[505,434],"class_list":["post-4818","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-memory-model","tag-acquire-release-semantic","tag-atomics"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4818","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=4818"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4818\/revisions"}],"predecessor-version":[{"id":6964,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/4818\/revisions\/6964"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/4813"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=4818"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=4818"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=4818"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}