{"id":5200,"date":"2017-02-25T05:47:28","date_gmt":"2017-02-25T05:47:28","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/latches-and-barriers\/"},"modified":"2023-06-26T12:21:29","modified_gmt":"2023-06-26T12:21:29","slug":"latches-and-barriers","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/latches-and-barriers\/","title":{"rendered":"Latches And Barriers"},"content":{"rendered":"<p>Latches and barriers are simple thread synchronization mechanisms, enabling some threads to wait until a counter becomes zero. We will, presumably in C++20, get latches and barriers in three variations: <span style=\"font-family: courier new,courier;\">std::latch,<\/span> <span style=\"font-family: courier new,courier;\">std::barrier<\/span>, and <span style=\"font-family: courier new,courier;\">std::flex_barrier.<\/span><\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-5199\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png\" alt=\"TimelineCpp20\" width=\"650\" height=\"224\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20.png 894w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20-300x103.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2017\/02\/TimelineCpp20-768x265.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>First, there are two questions:<\/p>\n<ol>\n<li>What are the differences between these three mechanisms to synchronize threads? You can use a <span style=\"font-family: Courier New,Courier,monospace;\">std::latch <\/span>only once, but you can use a <span style=\"font-family: Courier New,Courier,monospace;\">std::barrier<\/span> and a <span style=\"font-family: courier new;\">std::flex_barrier<\/span> more than once. Additionally,&nbsp; a <span style=\"font-family: courier new;\">std::flex_barrier<\/span> enables you to execute a function when the counter becomes zero.<\/li>\n<li>What use cases do latches and barriers support that can not be done in C++11 and C++14 with futures, threads, or condition variables in combinations with locks? Latches and barriers provide no new use cases, but they are much easier to use. They are also more performant because they often use a lock-free mechanism internally.<\/li>\n<\/ol>\n<p>Now, I will have a closer look at the three coordination mechanisms.<em>&nbsp;<\/em><br \/> <em> <\/em><\/p>\n<\/p>\n<h2>std::latch<\/h2>\n<p><span style=\"font-family: courier new,courier;\">std::latch<\/span> is a counter that counts down. Its value is set in the constructor. A thread can decrement the counter by using the method<span style=\"font-family: courier new,courier;\"><\/span> <span style=\"font-family: courier new,courier;\"><strong>thread.count_down_and_wait<\/strong><\/span> and wait until the counter becomes zero. In addition, the method <span style=\"font-family: courier new,courier;\"><strong>thread.count_down<\/strong><\/span> only decreases the counter by one without waiting. <span style=\"font-family: courier new;\">std::latch<\/span> has further the method<span style=\"font-family: courier new,courier;\"> <\/span><span style=\"font-family: courier new,courier;\"><strong>thread.is_ready<\/strong><\/span> to test if the counter is zero, and it has the method <strong><span style=\"font-family: courier new,courier;\">thread.wait<\/span> <\/strong>to wait until the counter becomes zero. <span style=\"font-family: courier        new,courier;\"><\/span>You cannot increment or reset the counter of a<span style=\"font-family: courier new;\"> std::latch. Hence<\/span> you can not reuse it.&nbsp;<\/p>\n<p>For further details to <span style=\"font-family: Courier New,Courier,monospace;\">std::latch<\/span> read the documentation on<span style=\"font-family: courier new,courier;\"> <\/span><a href=\"http:\/\/en.cppreference.com\/w\/cpp\/experimental\/latch\">cppreference.com<\/a>.<\/p>\n<p><span style=\"font-family: courier new,courier;\"><\/span>Here is a short code snippet from proposal <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2014\/n4204.html#h.9fyt9rmi4g2z\">n4204<\/a>.<\/p>\n<p>&nbsp;<\/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<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #2b91af;\">void<\/span> DoWork(threadpool* pool) {\r\n    latch completion_latch(NTASKS);\r\n    <span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #2b91af;\">int<\/span> i = 0; i &lt; NTASKS; ++i) {\r\n      pool-&gt;add_task([&amp;] {\r\n        <span style=\"color: #008000;\">\/\/ perform work<\/span>\r\n        ...\r\n        completion_latch.count_down();\r\n      }));\r\n    }\r\n    <span style=\"color: #008000;\">\/\/ Block until work is done<\/span>\r\n    completion_latch.wait();\r\n  }\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>I set the <span style=\"font-family: courier new,courier;\">std::latch completion_latch<\/span> in its constructor to <span style=\"font-family: courier new,courier;\">NTASKS<\/span> (line 2). The thread pool executes <span style=\"font-family: courier        new,courier;\">NTASKS<\/span> (lines 4 &#8211; 8).&nbsp; The counter will be decreased at the end of each task (line 7). Line 11 is the barrier for the thread running the function <span style=\"font-family: courier new,courier;\">DoWork<\/span> and, hence, for the small workflow. This thread has to wait until all tasks have been done.<\/p>\n<p>&nbsp;<\/p>\n<p><em>The proposal uses a <span style=\"font-family: courier new,courier;\">vector&lt;thread*&gt;<\/span> and pushes the dynamically allocated threads onto the vector <span style=\"font-family: courier new,courier;\">workers.push_back(new thread([&amp;]<\/span> {. That is a memory leak. <\/em><em>Instead, you should put the threads into a <span style=\"font-family: courier new,courier;\">std::unique_ptr<\/span> or directly create them in the vector: <span style=\"font-family: courier new,courier;\">workers.emplace_back[&amp;]{<\/span> . This observation holds for the example to the <span style=\"font-family: courier new,courier;\">std::barrier<\/span> and the <span style=\"font-family: courier new,courier;\">std::flex_barrier.<\/span><br \/><\/em><\/p>\n<h2>std::barrier<\/h2>\n<p>A <span style=\"font-family: courier new,courier;\">std::barrier<\/span> is quite similar to a <span style=\"font-family: courier new,courier;\">std::latch<\/span>. The subtle difference is that you can use a&nbsp; <span style=\"font-family: courier new,courier;\">std::barrier<\/span> more than once because the counter will be reset to its previous value. Immediately after the counter becomes zero, the so-called completion phase starts. This completion phase is in the case of a <span style=\"font-family: courier new,courier;\">std::barrier<\/span> empty. That changes with a&nbsp; <span style=\"font-family: courier new,courier;\">std::flex_barrier.<\/span> <span style=\"font-family: courier new,courier;\">std::barrier<\/span> has two exciting methods: <span style=\"font-family: courier new,courier;\">std::arrive_and_wait <\/span>and <span style=\"font-family: courier new,courier;\">std::arrive_and_drop. <\/span>While <strong><span style=\"font-family: courier          new,courier;\">std::arrive_and_wait<\/span><\/strong> waits at the synchronization point, <span style=\"font-family: courier new,courier;\"><strong>std::arrive_and_drop<\/strong><\/span> removes itself from the synchronization mechanism.<\/p>\n<p>Before I look at the <span style=\"font-family: courier        new,courier;\">std::flex_barrier <span style=\"font-family: Helvetica,Arial,sans-serif;\">and the completion phase, I will give a short example of the <\/span>std::barrier.<\/span><span style=\"font-family: courier new,courier;\"><\/span><span style=\"font-family: courier new,courier;\"><\/span><span style=\"font-family: courier new,courier;\"><\/span><\/p>\n<p>&nbsp;<\/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<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #2b91af;\">void<\/span> DoWork() {\r\n    Tasks&amp; tasks;\r\n    <span style=\"color: #2b91af;\">int<\/span> n_threads;\r\n    vector&lt;<span style=\"color: #0000ff;\">thread<\/span>*&gt; workers;\r\n\r\n    barrier task_barrier(n_threads);\r\n\r\n    <span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #2b91af;\">int<\/span> i = 0; i &lt; n_threads; ++i) {\r\n      workers.push_back(<span style=\"color: #0000ff;\">new<\/span> <span style=\"color: #0000ff;\">thread<\/span>([&amp;] {\r\n        <span style=\"color: #2b91af;\">bool<\/span> active = true;\r\n        <span style=\"color: #0000ff;\">while<\/span>(active) {\r\n          Task task = tasks.get();\r\n          <span style=\"color: #008000;\">\/\/ perform task<\/span>\r\n          ...\r\n          task_barrier.arrive_and_wait();\r\n         }\r\n       });\r\n    }\r\n    <span style=\"color: #008000;\">\/\/ Read each stage of the task until all stages are complete.<\/span>\r\n    <span style=\"color: #0000ff;\">while<\/span> (!finished()) {\r\n      GetNextStage(tasks);\r\n    }\r\n  }\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The <span style=\"font-family: courier new,courier;\">std::barrier barrier<\/span> in line 6 coordinates several threads that perform their tasks a few times. The number of threads is <span style=\"font-family: courier new,courier;\">n_threads <\/span>(line 3). Each thread takes its task (line 12) via <span style=\"font-family: courier new,courier;\"><span style=\"font-family: courier new,courier;\">task.get(), <\/span><\/span>performs it, and waits &#8211; as far as it is done with its task(line 15) &#8211; until all threads have done their task. After that, it takes a new task in line 12 as far as <span style=\"font-family: courier new;\">active <\/span>returns <span style=\"font-family: courier new;\">true <\/span>in line 12.<span style=\"font-family: courier new,courier;\"> <\/span><\/p>\n<h2>std::flex_barrier<\/h2>\n<p><em>From my perspective, the names in the example of the <span style=\"font-family: courier new,courier;\">std::flex_barrier<\/span> are slightly confusing. For example, the <span style=\"font-family: courier new,courier;\">std::flex_barrier<\/span> is called <span style=\"font-family: courier new,courier;\">notifying_barrier<\/span>. Therefore I used the name <span style=\"font-family: courier          new,courier;\">std::flex_barrier<\/span>.<\/em><\/p>\n<p>The <span style=\"font-family: courier new,courier;\">std::flex_barrier<\/span> has, in contrast to the <span style=\"font-family: courier new,courier;\">std::barrier<\/span> an additional constructor. This constructor can be parametrized by a callable unit that will be invoked in the completion phase. The callable unit has to return a number. This number sets the value of the counter in the completion phase. A number of -1 means that the counter keeps the same in the next iteration. Smaller numbers than -1 are not allowed.&nbsp;<\/p>\n<p>What is happening in the completion phase?<\/p>\n<ol>\n<li>All threads are blocked.<\/li>\n<li>A thread is unblocked and executes the callable unit.<\/li>\n<li>If the completion phase is done, all threads will be unblocked.<\/li>\n<\/ol>\n<p>The code snippet shows the usage of a <span style=\"font-family: courier        new,courier;\">std::flex_barrier.<\/span><\/p>\n<p>&nbsp;<\/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<\/pre>\n<\/td>\n<td>\n<pre style=\"margin: 0; line-height: 125%;\"> <span style=\"color: #2b91af;\">void<\/span> DoWork() {\r\n    Tasks&amp; tasks;\r\n    <span style=\"color: #2b91af;\">int<\/span> initial_threads;\r\n    atomic&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; current_threads(initial_threads);\r\n    vector&lt;<span style=\"color: #0000ff;\">thread<\/span>*&gt; workers;\r\n\r\n    <span style=\"color: #008000;\">\/\/ Create a flex_barrier, and set a lambda that will be<\/span>\r\n    <span style=\"color: #008000;\">\/\/ invoked every time the barrier counts down. If one or more<\/span>\r\n    <span style=\"color: #008000;\">\/\/ active threads have completed, reduce the number of threads.<\/span>\r\n    std::function rf = [&amp;] { <span style=\"color: #0000ff;\">return<\/span> current_threads;};\r\n    flex_barrier task_barrier(n_threads, rf);\r\n\r\n    <span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #2b91af;\">int<\/span> i = 0; i &lt; n_threads; ++i) {\r\n      workers.push_back(<span style=\"color: #0000ff;\">new<\/span> <span style=\"color: #0000ff;\">thread<\/span>([&amp;] {\r\n        <span style=\"color: #2b91af;\">bool<\/span> active = true;\r\n        <span style=\"color: #0000ff;\">while<\/span>(active) {\r\n          Task task = tasks.get();\r\n          <span style=\"color: #008000;\">\/\/ perform task<\/span>\r\n          ...\r\n          <span style=\"color: #0000ff;\">if<\/span> (finished(task)) {\r\n            current_threads--;\r\n            active = false;\r\n          }\r\n          task_barrier.arrive_and_wait();\r\n         }\r\n       });\r\n    }\r\n\r\n    <span style=\"color: #008000;\">\/\/ Read each stage of the task until all stages are complete.<\/span>\r\n    <span style=\"color: #0000ff;\">while<\/span> (!finished()) {\r\n      GetNextStage(tasks);\r\n    }\r\n  }\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The example follows a similar strategy as the example to <span style=\"font-family: courier new,courier;\">std::barrier. <\/span>The difference is that this time the counter of the <span style=\"font-family: courier new,courier;\">std::flex_barrier<\/span> is adjusted during run time; therefore, the <span style=\"font-family: courier new,courier;\">std::flex_barrier task_barrier<\/span> in line 11 gets a lambda function. This lambda function captures its variable <span style=\"font-family: courier        new,courier;\">current_thread<\/span> by reference. The variable will be decremented in line 21, and <span style=\"font-family: courier new;\">active <\/span>will be set to <span style=\"font-family: courier new;\">false <\/span>if the thread has done its task; therefore, the counter is decreased in the completion phase.<span style=\"font-family: courier new,courier;\"><br \/> <\/span><\/p>\n<p>A <span style=\"font-family: courier new,courier;\">std::flex_barrier<\/span> has one specialty in contrast to a <span style=\"font-family: courier new,courier;\">std::barrier<\/span> and a <span style=\"font-family: courier new,courier;\">std::latch.<\/span> This is the only one for which you can increase the counter.<\/p>\n<p>&nbsp;<\/p>\n<p>Read the details to <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/experimental\/latch\"><span style=\"font-family: courier new,courier;\">std::latch<\/span><\/a><span style=\"font-family: courier new,courier;\">, <\/span><span style=\"font-family: courier new,courier;\"><a href=\"http:\/\/en.cppreference.com\/w\/cpp\/experimental\/barrier\">std::barrier<\/a><\/span> , and <span style=\"font-family: courier new,courier;\"><a href=\"http:\/\/en.cppreference.com\/w\/cpp\/experimental\/flex_barrier\">std::flex_barrier<\/a> <\/span>at cppreference.com.<em><br \/> <\/em><\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>Coroutines are generalized functions that can be suspended and resumed while keeping their state. They are often used to implement cooperative tasks in operating systems, event loops in event systems, infinite lists, or pipelines. You can read the details about coroutines in the<a href=\"https:\/\/www.modernescpp.com\/index.php\/coroutines\"> next post<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Latches and barriers are simple thread synchronization mechanisms, enabling some threads to wait until a counter becomes zero. We will, presumably in C++20, get latches and barriers in three variations: std::latch, std::barrier, and std::flex_barrier.<\/p>\n","protected":false},"author":21,"featured_media":5199,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[367],"tags":[449,450],"class_list":["post-5200","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-multithreading-c-17-and-c-20","tag-barriers","tag-latches"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5200","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=5200"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5200\/revisions"}],"predecessor-version":[{"id":6883,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5200\/revisions\/6883"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media\/5199"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5200"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5200"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5200"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}