<html><head><title>Supplements to C++ Latches</title><meta content="text/html; charset=UTF-8" http-equiv="content-type"><style type="text/css">.lst-kix_list_1-1>li:before{content:"\002022  "}.lst-kix_list_2-8>li:before{content:"\002022  "}.lst-kix_list_3-2>li:before{content:"\002022  "}.lst-kix_list_3-7>li:before{content:"\002022  "}.lst-kix_list_2-0>li:before{content:"\002022  "}.lst-kix_list_1-2>li:before{content:"\002022  "}.lst-kix_list_1-5>li:before{content:"\002022  "}.lst-kix_list_2-3>li:before{content:"\002022  "}.lst-kix_list_2-4>li:before{content:"\002022  "}.lst-kix_list_3-5>li:before{content:"\002022  "}.lst-kix_list_1-4>li:before{content:"\002022  "}.lst-kix_list_3-0>li:before{content:"\002022  "}.lst-kix_list_1-0>li:before{content:"\002022  "}.lst-kix_list_2-5>li:before{content:"\002022  "}.lst-kix_list_2-7>li:before{content:"\002022  "}.lst-kix_list_1-8>li:before{content:"\002022  "}.lst-kix_list_3-4>li:before{content:"\002022  "}.lst-kix_list_1-3>li:before{content:"\002022  "}.lst-kix_list_3-3>li:before{content:"\002022  "}.lst-kix_list_3-6>li:before{content:"\002022  "}.lst-kix_list_3-8>li:before{content:"\002022  "}ul.lst-kix_list_3-7{list-style-type:none}ul.lst-kix_list_3-8{list-style-type:none}.lst-kix_list_1-6>li:before{content:"\002022  "}.lst-kix_list_2-6>li:before{content:"\002022  "}ul.lst-kix_list_3-0{list-style-type:none}ul.lst-kix_list_3-1{list-style-type:none}ul.lst-kix_list_3-2{list-style-type:none}ul.lst-kix_list_3-3{list-style-type:none}ul.lst-kix_list_3-4{list-style-type:none}.lst-kix_list_3-1>li:before{content:"\002022  "}ul.lst-kix_list_3-5{list-style-type:none}ul.lst-kix_list_3-6{list-style-type:none}ul.lst-kix_list_1-0{list-style-type:none}ul.lst-kix_list_1-2{list-style-type:none}ul.lst-kix_list_2-4{list-style-type:none}.lst-kix_list_2-2>li:before{content:"\002022  "}ul.lst-kix_list_1-1{list-style-type:none}ul.lst-kix_list_2-5{list-style-type:none}ul.lst-kix_list_1-4{list-style-type:none}ul.lst-kix_list_2-6{list-style-type:none}.lst-kix_list_1-7>li:before{content:"\002022  "}ul.lst-kix_list_1-3{list-style-type:none}ul.lst-kix_list_2-7{list-style-type:none}ul.lst-kix_list_2-0{list-style-type:none}ul.lst-kix_list_1-6{list-style-type:none}ul.lst-kix_list_2-1{list-style-type:none}ul.lst-kix_list_1-5{list-style-type:none}ul.lst-kix_list_2-2{list-style-type:none}ul.lst-kix_list_1-8{list-style-type:none}ul.lst-kix_list_1-7{list-style-type:none}ul.lst-kix_list_2-3{list-style-type:none}.lst-kix_list_2-1>li:before{content:"\002022  "}ul.lst-kix_list_2-8{list-style-type:none}ol{margin:0;padding:0}.c30{border-bottom-width:1pt;border-top-style:solid;width:398pt;border-right-style:solid;padding:1.4pt 1.4pt 1.4pt 1.4pt;border-bottom-color:#000000;border-top-width:1pt;border-bottom-style:solid;vertical-align:middle;border-top-color:#000000;border-left-color:#000000;border-right-color:#000000;border-left-style:solid;border-right-width:1pt;border-left-width:1pt}.c21{border-bottom-width:1pt;border-top-style:solid;width:60.8pt;border-right-style:solid;padding:1.4pt 1.4pt 1.4pt 1.4pt;border-bottom-color:#000000;border-top-width:1pt;border-bottom-style:solid;vertical-align:middle;border-top-color:#000000;border-left-color:#000000;border-right-color:#000000;border-left-style:solid;border-right-width:1pt;border-left-width:1pt}.c32{border-bottom-width:1pt;border-top-style:solid;width:38.7pt;border-right-style:solid;padding:1.4pt 1.4pt 1.4pt 1.4pt;border-bottom-color:#000000;border-top-width:1pt;border-bottom-style:solid;vertical-align:middle;border-top-color:#000000;border-left-color:#000000;border-right-color:#000000;border-left-style:solid;border-right-width:1pt;border-left-width:1pt}.c4{vertical-align:baseline;color:#000000;font-size:14pt;font-style:normal;font-family:"Times New Roman";text-decoration:none;font-weight:normal}.c11{vertical-align:baseline;color:#000000;font-style:normal;text-decoration:none;font-weight:normal}.c0{widows:2;orphans:2;direction:ltr;margin-left:28.4pt;padding-bottom:14.2pt}.c19{vertical-align:baseline;color:#000000;font-size:14pt;text-decoration:none;font-weight:normal}.c22{vertical-align:baseline;font-size:14pt;font-style:normal;font-weight:normal}.c24{margin-right:auto;border-collapse:collapse}.c2{widows:2;orphans:2;direction:ltr}.c31{max-width:540pt;background-color:#ffffff;padding:21.6pt 36pt 21.6pt 36pt}.c6{color:#1155cc;text-decoration:underline}.c1{font-size:12pt;font-family:"Courier New"}.c8{color:inherit;text-decoration:inherit}.c7{font-size:10pt;font-family:"Courier New"}.c12{line-height:1.0;text-align:left}.c15{height:14pt}.c29{font-size:14pt}.c20{margin-left:72pt}.c10{padding-bottom:14.2pt}.c16{margin-left:54pt}.c13{font-size:12pt}.c18{font-family:"Times New Roman"}.c5{padding-top:0pt}.c26{margin-left:18pt}.c9{page-break-after:avoid}.c28{height:0pt}.c25{padding-bottom:6pt}.c17{padding-bottom:0pt}.c27{font-size:10pt}.c3{font-style:italic}.c14{margin-left:36pt}.c23{color:#000000}.title{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:center;color:#000000;font-size:16pt;font-family:"Arial";font-weight:bold;padding-bottom:3pt}.subtitle{widows:2;padding-top:0pt;line-height:1.0;orphans:2;text-align:center;color:#000000;font-size:14pt;font-family:"Arial";padding-bottom:3pt}li{color:#000000;font-size:14pt;font-family:"Times New Roman"}p{color:#000000;font-size:14pt;margin:0;font-family:"Times New Roman"}h1{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-size:24pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:6pt;page-break-after:avoid}h2{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-size:18pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:6pt;page-break-after:avoid}h3{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-size:14pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:6pt;page-break-after:avoid}h4{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-size:14pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:6pt;page-break-after:avoid}h5{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-style:italic;font-size:13pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:3pt}h6{widows:2;padding-top:12pt;line-height:1.0;orphans:2;text-align:left;color:#000000;font-size:11pt;font-family:"Times New Roman";font-weight:bold;padding-bottom:3pt}</style></head><body class="c31"><h1 class="c2 c9"><a name="h.mrc6952e0whh"></a><span>Supplements to </span><span class="c18 c23">C++ Latche</span><span>s</span></h1><p class="c2 c12 c5 c25"><span class="c4">ISO/IEC JTC1 SC22 WG21 N</span><span>4224</span><span class="c4">&nbsp;- 2014-</span><span>10</span><span class="c4">-</span><span>10</span></p><p class="c2 c12 c5 c25"><span class="c4">Alasdair Mackintosh, </span><span class="c22 c6 c18"><a class="c8" href="mailto:alasdair@google.com">alasdair@google.com</a></span><span class="c4">, </span><span class="c6 c18 c22"><a class="c8" href="mailto:alasdair.mackintosh@gmail.com">alasdair.mackintosh@gmail.com</a></span></p><p class="c2 c12 c5 c25"><span>Adam Berkan, </span><span class="c6"><a class="c8" href="mailto:aberkan@google.com">aberkan@google.com</a></span></p><p class="c2 c12 c15 c5 c25"><span></span></p><p class="c2 c26"><span class="c6"><a class="c8" href="#h.mrc6952e0whh">Supplements to C++ Latches</a></span></p><p class="c2 c16"><span class="c6"><a class="c8" href="#h.qfiyr9nhb3on">Revision History</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.r7uhgh9a3qqw">Introduction</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.ec4ih7e6p6yi">Problem</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.rs9bik786ahx">Solution</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.hspmumxhdycg">Self-Deleting Latches</a></span></p><p class="c2 c16"><span class="c6"><a class="c8" href="#h.pzzu8scuekru">Header std::latch Synopsis</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.d39iar4by47d">Flex Latch</a></span></p><p class="c2 c16"><span class="c6"><a class="c8" href="#h.ochtd04q8zj5">Header std::flex_latch Synopsis</a></span></p><p class="c2 c20"><span class="c6"><a class="c8" href="#h.4kr7i3465ny5">Memory Ordering</a></span></p><p class="c2 c14"><span class="c6"><a class="c8" href="#h.tvlkvmc0tfwt">Using Self-Deleting Flex Latches</a></span></p><h3 class="c2 c9"><a name="h.qfiyr9nhb3on"></a><span class="c18 c23">Revision History</span></h3><a href="#" name="9dff1b7ed9f21f7b15102aee78281c5efbb3d942"></a><a href="#" name="0"></a><table cellpadding="0" cellspacing="0" class="c24"><tbody><tr class="c28"><td class="c32" colspan="1" rowspan="1"><p class="c2 c12 c5 c17"><span class="c11 c13 c18">N</span><span class="c13">4224</span></p></td><td class="c21" colspan="1" rowspan="1"><p class="c2 c12 c5 c17"><span class="c11 c13 c18">201</span><span class="c13">4</span><span class="c11 c13 c18">-</span><span class="c13">10</span><span class="c11 c13 c18">-1</span><span class="c13">0</span></p></td><td class="c30" colspan="1" rowspan="1"><p class="c2 c12 c5 c17"><span class="c11 c13 c18">Initial Version</span></p></td></tr></tbody></table><a href="#" name="id.a305bnrba98x"></a><h2 class="c2 c9"><a name="h.r7uhgh9a3qqw"></a><span class="c18 c23">Introduction</span></h2><p class="c2 c12 c5 c25"><span>This paper adds two new independent concepts to &quot;C++ Latches and Barriers&quot; (N4204, previously N3998). Readers are assumed to be familiar with the terminology and concepts from that paper.</span></p><a href="#" name="id.fh0v90zi0zci"></a><h2 class="c2 c9"><a name="h.ec4ih7e6p6yi"></a><span>Problem</span></h2><p class="c2"><span>A latch cannot safely be destroyed until all co-ordinating threads have arrived at the synchronization point. This effectively means that a thread must wait on the latch before destroying it. Typical </span><span>code </span><span>may look like this.</span></p><p class="c2"><span class="c7">&nbsp; void DoWork() {<br> &nbsp; &nbsp;latch completion_latch(NTASKS);<br> &nbsp; &nbsp;for (int i = 0; i &lt; NTASKS; ++i) {<br> &nbsp; &nbsp; &nbsp;// add task...<br> &nbsp; &nbsp;}<br> &nbsp; &nbsp;// Must block this thread until work is done<br> &nbsp; &nbsp;completion_latch.wait();<br> &nbsp;}</span></p><p class="c2"><span>L</span><span>atches would be often be easier to use if they were automatically destroyed when the last waiting thread left the latch</span><span>.</span></p><p class="c2 c15"><span></span></p><p class="c2"><span>In addition, it is impossible, using a single latch, to invoke a function after all threads have </span><span>arrived at the synchronization point, but before any threads are released. It is possible to write:</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; latch start_latch(NTASKS);<br> &nbsp; &nbsp;for (int i = 0; i &lt; NTASKS; ++i) {<br> &nbsp; &nbsp; &nbsp;pool-&gt;add_task([&amp;] {<br> &nbsp; &nbsp; &nbsp; &nbsp;PrepareWorker();<br> &nbsp; &nbsp; &nbsp; &nbsp;start_latch.arive_and_wait();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;DoWork();<br> &nbsp; &nbsp; &nbsp;}));</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; }<br> &nbsp; &nbsp;start_latch.wait();<br> &nbsp; &nbsp;cout &lt;&lt; &quot;workers about to start\n&quot;</span></p><p class="c2"><span class="c7">&nbsp; }</span></p><p class="c2"><span>But by the time</span><span>&nbsp;the message is printed, the workers may already have started. To ensure that the message is printed before the workers start, a second latch would be required.</span></p><p class="c2 c15"><span></span></p><a href="#" name="id.30j0zll"></a><h2 class="c2 c9"><a name="h.rs9bik786ahx"></a><span class="c18 c23">Solution</span></h2><p class="c2"><span>To avoid the thread that creates the latch having to block on </span><span class="c7">wait()</span><span>, we propose a modification to the latch class to allow self-deleting latches to be created. </span></p><p class="c2"><span>To allow a completion function to run at the appropriate time, we </span><span class="c4">propose a</span><span>&nbsp;flex latch, similar to the flex barrier. </span></p><p class="c2"><span>These proposals can be added independently. Note, however, that there may be benefits to combining them. See the section &quot;Using Self-Deleting Flex Latches&quot; below.</span></p><h2 class="c2 c9"><a name="h.hspmumxhdycg"></a><span>Self-Deleting Latches</span></h2><p class="c2 c5"><span>A self-deleting latch destroys itself after the synchronization point</span><span>&nbsp;has been reached</span><span>. It is not necessary for a thread to wait on the latch before destroying it. Sample usage:</span></p><p class="c2"><span class="c7">&nbsp; void DoWork(threadpool* pool) {<br> &nbsp; &nbsp;latch* completion_latch = latch::create_self_deleting(NTASKS);<br> &nbsp; &nbsp;for (int i = 0; i &lt; NTASKS; ++i) {<br> &nbsp; &nbsp; &nbsp;pool-&gt;add_task([&amp;] {<br> &nbsp; &nbsp; &nbsp; &nbsp;// perform work (part 1)<br> &nbsp; &nbsp; &nbsp; &nbsp;...</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; &nbsp; &nbsp; completion_latch-&gt;</span><span class="c7">arrive_and_wait</span><span class="c7">();</span></p><p class="c2"><span class="c7">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // perform work (part 2), knowing that</span></p><p class="c2"><span class="c7">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // all threads have performed part 1.<br> &nbsp; &nbsp; &nbsp; &nbsp;...</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; &nbsp; }));</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; }</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; // May return here - completion_latch will be destroyed<br> &nbsp; &nbsp;// when part 1 has finished</span></p><p class="c2"><span class="c7">&nbsp; }</span></p><p class="c2 c15 c5"><span class="c7"></span></p><p class="c2 c5"><span>After all threads have exited the functions that caused the synchronization point to be reached, a self-deleting latch is guaranteed to have been destroyed, and its pointer should not be used.</span></p><p class="c2"><span>Otherwise, a self-deleting latch behaves as a normally constructed latch, with the exception that the </span><span class="c7">wait()/try_wait() </span><span>functions may not be invoked</span><span>&nbsp;on a self-deleting latch.</span><span>&nbsp;</span><span>(Because the latch will destroy itself when the last thread reaches the completion, it would only be possible to call </span><span class="c7">wait()</span><span>&nbsp;by synchronizing with the worker threads using the latch. If this is necessary, the latch can be created with a count N+1, and the co-ordinating thread can use </span><span class="c7">arrive_and_wait()</span><span>. In practice, a self-deleting latch is only useful when no thread is going to call </span><span class="c7">wait()</span><span>, so we do not feel that this restriction is significant.)</span></p><p class="c2 c5 c15"><span></span></p><p class="c2 c5"><span>Note: Although it would be possible to implement the same functionality using</span><span class="c7">&nbsp;std::</span><span class="c7">shared_ptr</span><span class="c7">&lt;std::latch&gt;</span><span>, we believe that the self-deleting latch is cleaner, and has less overhead.</span></p><h3 class="c2 c9"><a name="h.pzzu8scuekru"></a><span>Header std::latch Synopsis</span></h3><p class="c2 c5"><span>The synopsis is as in N4204, with the following amendments:<br></span></p><p class="c2 c10 c5"><span class="c1">static latch* create_self_deleting( </span><span class="c1">int</span><span class="c1">&nbsp;</span><span class="c1">C</span><span class="c1">&nbsp;);</span></p><p class="c0 c5"><span class="c3">Requires</span><span>: C shall be &gt;= zero.</span></p><p class="c0 c5"><span class="c3">Effects</span><span>: creates a new latch, initialized with a count of C, and returns a pointer to the new latch.</span><span>&nbsp; [Note: </span><span>If C is zero, the synchronization condition has been reached, and the returned pointer may not be used. End note]</span><span>. The </span></p><p class="c0 c5"><span class="c3">Synchronization: </span><span>None</span></p><p class="c2 c10 c5"><span class="c1">~latch( );</span></p><p class="c0 c5"><span class="c3">Requires:</span><span>&nbsp;This latch was not created with a call to </span><span class="c1">create_self_deleting</span><span class="c1">()</span><span class="c1">.</span><span>&nbsp;</span><span class="c3">[Additional wording as per N4204.]</span></p><p class="c2 c10 c5"><span class="c1">void arrive( );</span></p><p class="c0 c5"><span class="c3">Effects</span><span>: </span><span>Decrements</span><span>&nbsp;the internal count by 1. </span><span>If the count reaches 0 the synchronization condition is reached.</span><span>&nbsp;The </span><span>latch may have destroyed itself when this method returns if it was created with a call to </span><span class="c1">create_self_deleting</span><span class="c1">()</span><span class="c1">.</span><span>&nbsp;</span><span class="c3">[Additional wording as per N4204. Similar wording for count_down() and arrive_and_wait().]</span></p><p class="c2 c5 c10"><span class="c1">void wait( );</span></p><p class="c0 c5"><span class="c3">Requires:</span><span>&nbsp;This latch was not created with a call to </span><span class="c1">create_self_deleting</span><span class="c1">()</span><span class="c1">.</span><span>&nbsp;</span></p><p class="c0 c5"><span class="c3">Effects: [Additional wording as per N4204. Similar wording for try_wait().]</span></p><h2 class="c2 c9"><a name="h.d39iar4by47d"></a><span>Flex Latch</span></h2><p class="c2"><span>A variant of latch that takes a completion function.</span></p><h3 class="c2 c9"><a name="h.ochtd04q8zj5"></a><span>Header std::flex_latch Synopsis</span></h3><p class="c2"><span>Provides the Latch concept.</span></p><p class="c2"><span class="c4">A flex latch behaves as a la</span><span>tch, but is constructed with a callable completion function that is invoked after all threads have arrived at the </span><span class="c3">synchronization point</span><span>, and before the </span><span class="c3">synchronization condition</span><span>&nbsp;is reached.</span></p><p class="c2"><span>The flex barrier maintains </span><span class="c4">an internal counter that is initialized when the latch is created. The </span><span class="c19 c18 c3">synchronization condit</span><span class="c3">ion</span><span>&nbsp;is reached when the counter is decremented to 0. Threads may block at a </span><span class="c3">synchronization point</span><span>&nbsp;waiting for the condition to be reached.</span><span class="c3">&nbsp;</span><span>When the condition is reached, any such blocked threads will be released.</span></p><p class="c2 c12 c15 c5 c25"><span></span></p><p class="c2 c17"><span class="c1">template &lt;typename T&gt;</span></p><p class="c2 c12 c10 c5"><span class="c1">flex_latch</span><span class="c11 c1">( </span><span class="c1 c11">int</span><span class="c1">&nbsp;</span><span class="c11 c1">C</span><span class="c11 c1">, </span><span class="c11 c1">T F</span><span class="c11 c1">&nbsp;);</span></p><p class="c0 c12 c5"><span class="c3">Requires</span><span>: C shall be &gt;= zero. </span><span>F shall conform to the callable </span><span class="c1">void()</span><span>&nbsp;concept.</span></p><p class="c0 c12 c5"><span class="c3">Effects</span><span>: initializes the latch with a count of C</span><span>, and a callable object that will be invoked after</span><span>&nbsp;the </span><span class="c3">synchronization condition</span><span>&nbsp;is reached.</span><span>&nbsp; </span><span>[Note: </span><span>If C is zero, the synchronization condition has been </span><span>reached</span><span>. End note]</span></p><p class="c0 c12 c5"><span class="c3">Synchronization: </span><span>None</span></p><p class="c2 c12 c10 c5"><span class="c1">~flex_latch</span><span class="c11 c1">( );</span></p><p class="c0"><span class="c3">Requires:</span><span>&nbsp;No threads are blocked at the synchronization </span><span>point</span><span>. May </span><span class="c4">be </span><span>called</span><span class="c4">&nbsp;if threads have not yet returned from </span><span class="c1">wait()</span><span>&nbsp;or </span><span class="c1">arrive_and_wait()</span><span>&nbsp;provided that the condition has been reached.</span><span>&nbsp; [Note: The destructor may not return until all threads have exited </span><span>&nbsp;</span><span class="c1">wait()</span><span>&nbsp;or </span><span class="c1">arrive_and_wait()</span><span>. End note]</span></p><p class="c2 c10"><span class="c1">void arrive( );</span></p><p class="c0"><span class="c3">Effects</span><span>: </span><span>Decrements</span><span>&nbsp;the internal count by 1. </span><span>If the count reaches 0 the</span><span>&nbsp;synchronization condition is reached.</span><span>&nbsp;</span><span>&nbsp;Before any threads are released, the callable completion object registered in the constructor will be </span><span>invoked</span><span>. (The completion will be invoked in the context of one of the threads that invoked </span><span class="c1">arrive()</span><span>&nbsp;or </span><span class="c1">arrive_and_wait()</span><span>.) &nbsp;</span><span>I</span><span>f called more than once by a given thread the behaviour is undefined.</span></p><p class="c0"><span class="c3">Throws:</span><span>&nbsp;std::logic_error if the internal count would be decremented below 0.</span></p><p class="c0"><span class="c3">Synchronization: </span><span>Synchronizes with invocations of </span><span>the</span><span>&nbsp;completion function. Invocations of the c</span><span>ompletion</span><span>&nbsp;function then synchronize with calls unblocked as a result of this call.</span></p><p class="c2 c10"><span class="c1">void arrive_and_wait( );</span></p><p class="c0"><span class="c3">Effects</span><span>: </span><span>Decrements the internal count by 1. If the count reaches 0 </span><span>the synchronization condition is reached. Before any threads are released, the callable completion object registered in the constructor will be invoked. (The completion will be invoked in the context of one of the threads that invoked </span><span class="c1">arrive()</span><span>&nbsp;or </span><span class="c1">arrive_and_wait()</span><span>.) Otherwise b</span><span>locks</span><span>&nbsp;at the synchronization point until the synchronization condition is reached. If called more than once by a given thread the behaviour is undefined.</span></p><p class="c0"><span class="c3">Throws:</span><span>&nbsp;std::logic_error if the internal count would be decremented below 0.</span></p><p class="c0"><span class="c3">Synchronization: </span><span>Synchronizes with calls unblocked as a result of this call and try_wait calls on the same latch that return true as a result.</span></p><p class="c2 c12 c10 c5"><span class="c11 c1">void count_down( </span><span class="c1">int</span><span class="c11 c1">&nbsp;N );</span></p><p class="c0 c12 c5"><span class="c3">Effects</span><span>: </span><span class="c4">Decrements the internal count by </span><span>N</span><span class="c4">. If the count reaches 0, </span><span>the synchronization condition is reached</span><span class="c4">. </span><span>Before any threads are released, the callable completion object registered in the constructor will be invoked. (The completion will be invoked in the context of one of the threads that invoked </span><span class="c1">arrive()</span><span>&nbsp;or </span><span class="c1">arrive_and_wait()</span><span>.) </span><span class="c4">May be called by any thread. Does not block.</span></p><p class="c0 c12 c5"><span class="c18 c3 c19">Throws:</span><span class="c4">&nbsp;std::logic_error if the internal count </span><span>would be decremented below </span><span>0</span><span class="c4">.</span></p><p class="c0 c12 c5"><span class="c3">Synchronization: </span><span>Synchronizes with calls unblocked as a result of this call and try_wait calls on the same latch that return true as a result.</span></p><p class="c2 c12 c10 c5"><span class="c11 c1">void wait( );</span></p><p class="c0 c12 c5"><span class="c3">Effects</span><span>: </span><span class="c4">Blocks the calling thread at the synchronization point until the </span><span>synchronization condition is reached.</span><span class="c4">&nbsp;If the</span><span>&nbsp;condition has already been reached</span><span class="c4">, </span><span>&nbsp;the thread does not block.</span></p><p class="c2 c10"><span class="c1">bool try_wait( );</span></p><p class="c0 c5 c12"><span class="c3">Returns: </span><span class="c4">Returns true if the synchronization condition has been is reached, and false otherwise. Does not block.</span></p><p class="c2"><span class="c1">flex_latch(const flex_latch&amp;) = delete;<br>flex_latch&amp; operator=(const flex_latch&amp;) = delete;</span></p><h4 class="c2 c9"><a name="h.4kr7i3465ny5"></a><span class="c18 c23 c29">Memory Ordering</span></h4><p class="c2"><span>C</span><span>alls to </span><span class="c1">ar</span><span class="c1">rive, arrive_and_wait()</span><span>&nbsp;or </span><span class="c1">countdown</span><span class="c1">()</span><span>&nbsp;that triggered the comple</span><span>tion synchronize with invocation of the completion function.</span><span>&nbsp;The completion function synchronizes with all calls unblocked after it has run</span><span>.</span></p><p class="c2 c12 c15 c5 c25"><span></span></p><h2 class="c2 c9"><a name="h.tvlkvmc0tfwt"></a><span>Using </span><span>Self-Deleting Flex Latches</span></h2><p class="c2"><span>Consider the use-case where several threads perform a parallel operation. When all operations are complete, we wish to perform one final operation. With the current N4204 proposal, we must either have one additional thread that blocks on wait(), or we must designate one of the worker threads to perform the final operation. (This may be difficult if the threads are part of a threadpool, and we do not know which thread will run which task.)</span></p><p class="c2"><span>A self-deleting Flex Latch can be given a completion function that will be invoked when all threads have completed their task and arrived at the synchronization condition. When the completion finishes, the worker threads will continue, and the latch will be destroyed.</span></p><p class="c2 c15"><span></span></p><p class="c2"><span class="c7">&nbsp; void DoWork(threadpool* pool) {</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; flex_latch* completion_latch = flex_latch::create_self_deleting(<br> &nbsp; &nbsp; &nbsp;&amp;OnCompletion, NTASKS);<br> &nbsp; &nbsp;for (int i = 0; i &lt; NTASKS; ++i) {<br> &nbsp; &nbsp; &nbsp;pool-&gt;add_task([&amp;] {<br> &nbsp; &nbsp; &nbsp; &nbsp;// perform work<br> &nbsp; &nbsp; &nbsp; &nbsp;&hellip;<br> &nbsp; &nbsp; &nbsp; &nbsp;completion_latch-&gt;</span><span class="c7">count_down</span><span class="c7">();</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; &nbsp; }));</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; }</span></p><p class="c2"><span class="c7">&nbsp; &nbsp; // Return without blocking. OnCompletion will run when<br> &nbsp; &nbsp;// the threads have finished, and the latch will then be<br> &nbsp; &nbsp;// destroyed.</span></p><p class="c2"><span class="c7">&nbsp; }</span></p><p class="c2 c15"><span></span></p></body></html>