{"id":6078,"date":"2021-01-29T08:54:57","date_gmt":"2021-01-29T08:54:57","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/barriers-in-c-20\/"},"modified":"2023-06-26T09:33:28","modified_gmt":"2023-06-26T09:33:28","slug":"barriers-in-c-20","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/barriers-in-c-20\/","title":{"rendered":"Barriers and Atomic Smart Pointers in C++20"},"content":{"rendered":"<p>In my last post, I introduced latches in C++20. A latch enables its threads to wait until a counter becomes zero. Additionally to a latch, its big sibling barrier can be used more than once. Today, I write about barriers and present atomic smart pointers.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<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>If you are not familiar with std::latch, read my last post: <a href=\"https:\/\/bit.ly\/39eH23G\">Latches in C++20<\/a>.<\/p>\n<h2><code>std::barrier<\/code><\/h2>\n<p>There are two differences between a <code>std::latch<\/code> and a <code>std::barrier<\/code>. A <code>std::latch<\/code> is useful for managing one task by multiple threads; a<code> std::barrier<\/code> helps manage repeated tasks by multiple threads.&nbsp;Additionally, a <code>std::barrier<\/code> enables you to execute a function in the so-called completion step. The completion step is the state when the counter becomes zero. Immediately after the counter becomes zero, the so-called completion step starts. In this completion step, a callable is invoked. The <code>std::barrier<\/code> gets its callable in its constructor. A callable unit (short callable) behaves like a function. Not only are these named functions but also function objects or lambda expressions.<\/p>\n<p>The completion step performs the following steps:<\/p>\n<ol>\n<li>All threads are blocked.<\/li>\n<li>An arbitrary thread is unblocked and executes the callable.<\/li>\n<li>If the completion step is done, all threads are unblocked.<\/li>\n<\/ol>\n<p>The following table presents you with the interface of a <code>std::barrier bar.<\/code><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6075\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/barrier.png\" alt=\"barrier\" width=\"650\" height=\"222\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/barrier.png 965w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/barrier-300x102.png 300w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/barrier-768x262.png 768w\" sizes=\"auto, (max-width: 650px) 100vw, 650px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>The<code> call bar.arrive_and_drop()<\/code> call means essentially that the counter is decremented by one for the next phase. The following program<code> fullTimePartTimeWorkers.cpp<\/code> halves the number of workers in the second phase.<\/p>\n<p><!-- HTML generated using hilite.me --><\/p>\n<div style=\"background: #f0f3f3; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\"><span style=\"color: #0099ff; font-style: italic;\">\/\/ fullTimePartTimeWorkers.cpp<\/span>\r\n\r\n<span style=\"color: #009999;\">#include &lt;iostream&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;barrier&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;mutex&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;string&gt;<\/span>\r\n<span style=\"color: #009999;\">#include &lt;thread&gt;<\/span>\r\n\r\nstd<span style=\"color: #555555;\">::<\/span>barrier workDone(<span style=\"color: #ff6600;\">6<\/span>);\r\nstd<span style=\"color: #555555;\">::<\/span>mutex coutMutex;\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">synchronizedOut<\/span>(<span style=\"color: #006699; font-weight: bold;\">const<\/span> std<span style=\"color: #555555;\">::<\/span>string<span style=\"color: #555555;\">&amp;<\/span> s) noexcept {\r\n    std<span style=\"color: #555555;\">::<\/span>lock_guard<span style=\"color: #555555;\">&lt;<\/span>std<span style=\"color: #555555;\">::<\/span>mutex<span style=\"color: #555555;\">&gt;<\/span> lo(coutMutex);\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> s;\r\n}\r\n\r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">FullTimeWorker<\/span> {                                                   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (1)<\/span>\r\n <span style=\"color: #9999ff;\">public:<\/span>\r\n    FullTimeWorker(std<span style=\"color: #555555;\">::<\/span>string n)<span style=\"color: #555555;\">:<\/span> name(n) { };\r\n  \r\n    <span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">operator<\/span>() () {\r\n        synchronizedOut(name <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\": \"<\/span> <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\"Morning work done!<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>);\r\n        workDone.arrive_and_wait();  <span style=\"color: #0099ff; font-style: italic;\">\/\/ Wait until morning work is done     (3)<\/span>\r\n        synchronizedOut(name <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\": \"<\/span> <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\"Afternoon work done!<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>);\r\n        workDone.arrive_and_wait();  <span style=\"color: #0099ff; font-style: italic;\">\/\/ Wait until afternoon work is done   (4)<\/span>\r\n        \r\n    }\r\n <span style=\"color: #9999ff;\">private:<\/span>\r\n    std<span style=\"color: #555555;\">::<\/span>string name;\r\n};\r\n  \r\n<span style=\"color: #006699; font-weight: bold;\">class<\/span> <span style=\"color: #00aa88; font-weight: bold;\">PartTimeWorker<\/span> {                                                   <span style=\"color: #0099ff; font-style: italic;\">\/\/ (2)<\/span>\r\n <span style=\"color: #9999ff;\">public:<\/span>\r\n    PartTimeWorker(std<span style=\"color: #555555;\">::<\/span>string n)<span style=\"color: #555555;\">:<\/span> name(n) { };\r\n  \r\n    <span style=\"color: #007788; font-weight: bold;\">void<\/span> <span style=\"color: #cc00ff;\">operator<\/span>() () {\r\n        synchronizedOut(name <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\": \"<\/span> <span style=\"color: #555555;\">+<\/span> <span style=\"color: #cc3300;\">\"Morning work done!<\/span><span style=\"color: #cc3300; font-weight: bold;\">\\n<\/span><span style=\"color: #cc3300;\">\"<\/span>);\r\n        workDone.arrive_and_drop();  <span style=\"color: #0099ff; font-style: italic;\">\/\/ Wait until morning work is done  \/\/ (5)<\/span>\r\n    }\r\n <span style=\"color: #9999ff;\">private:<\/span>\r\n    std<span style=\"color: #555555;\">::<\/span>string name;\r\n};\r\n\r\n<span style=\"color: #007788; font-weight: bold;\">int<\/span> <span style=\"color: #cc00ff;\">main<\/span>() {\r\n\r\n    std<span style=\"color: #555555;\">::<\/span>cout <span style=\"color: #555555;\">&lt;&lt;<\/span> <span style=\"color: #cc3300;\">'\\n'<\/span>;\r\n\r\n    FullTimeWorker herb(<span style=\"color: #cc3300;\">\"  Herb\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> herbWork(herb);\r\n  \r\n    FullTimeWorker scott(<span style=\"color: #cc3300;\">\"    Scott\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> scottWork(scott);\r\n  \r\n    FullTimeWorker bjarne(<span style=\"color: #cc3300;\">\"      Bjarne\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> bjarneWork(bjarne);\r\n  \r\n    PartTimeWorker andrei(<span style=\"color: #cc3300;\">\"        Andrei\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> andreiWork(andrei);\r\n  \r\n    PartTimeWorker andrew(<span style=\"color: #cc3300;\">\"          Andrew\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> andrewWork(andrew);\r\n  \r\n    PartTimeWorker david(<span style=\"color: #cc3300;\">\"            David\"<\/span>);\r\n    std<span style=\"color: #555555;\">::<\/span><span style=\"color: #006699; font-weight: bold;\">thread<\/span> davidWork(david);\r\n\r\n    herbWork.join();\r\n    scottWork.join();\r\n    bjarneWork.join();\r\n    andreiWork.join();\r\n    andrewWork.join();\r\n    davidWork.join();\r\n  \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>This workflow consists of two kinds of workers: full-time workers (1) and part-time workers (2). The part-time worker works in the morning, and the full-time worker in the morning and the afternoon. Consequently, the full-time workers call<code> workDone.arrive_and_wait()<\/code> (lines (3) and (4)) two times. On the contrary, part-time works call <code>workDone.arrive_and_drop()<\/code> (5) only once. This <code>workDone.arrive_and_drop()<\/code> call causes the part-time worker to skip the afternoon work. Accordingly, the counter has in the first phase (morning) the value 6, and in the second phase (afternoon) the value 3.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6076\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/fullTimePartTimeWorkers.png\" alt=\"fullTimePartTimeWorkers\" width=\"500\" height=\"328\" style=\"display: block; margin-left: auto; margin-right: auto;\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/fullTimePartTimeWorkers.png 409w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/fullTimePartTimeWorkers-300x197.png 300w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/p>\n<p>Now to something I missed in my posts on atomics.<\/p>\n<\/p>\n<h2>Atomic Smart Pointers<\/h2>\n<p>A <code>std::shared_ptr<\/code> consists of a control block and its resource. The control block is thread-safe, but access to the resource is not. This means modifying the reference counter is an atomic operation, and you have the guarantee that the resource is deleted exactly once. These are the guarantees <code>std::shared_ptr<\/code> given you.<\/p>\n<p>On the contrary, it is crucial that a <code>std::shared_ptr<\/code> has well-defined multithreading semantics. At first glance, the use of a <code>std::shared_ptr<\/code> does not appear to be a sensible choice for multithreaded code. It is, by definition, shared and mutable and is the ideal candidate for non-synchronized read and write operations and hence for undefined behavior. On the other hand, there is a guideline in modern C++: <strong>Don&#8217;t use raw pointers<\/strong>. Consequently, you should use smart pointers in multithreading programs when you want to model shared ownership.<\/p>\n<p>The proposal <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2014\/n4162\">N4162<\/a> for atomic smart pointers directly addresses the deficiencies of the current implementation. The deficiencies boil down to these three points: consistency, correctness, and performance.<\/p>\n<ul>\n<li><strong>Consistency<\/strong>: the atomic operations&nbsp;&nbsp;<code>std::shared_ptr<\/code> are the only ones for a non-atomic data type.<\/li>\n<li><strong>Correctness<\/strong>: the usage of global atomic operations is quite error-prone because the correct usage is based on discipline. It is easy to forget to use an atomic operation &#8211; such as using <code>ptr = localPtr<\/code> instead of <code>std::atomic_store(&amp;ptr, localPt<\/code>r). The result is undefined behaviour because of a data race. If we used an atomic smart pointer instead, the type system would not allow it.<\/li>\n<li><strong>Performance<\/strong>: the atomic smart pointers have a significant advantage over the free <code>atomic_<\/code>* functions. The atomic versions are designed for the particular use case and can internally have a<code> std::atomic_flag<\/code> as a kind of cheap <a href=\"https:\/\/en.wikipedia.org\/wiki\/Spinlock\">spinlock<\/a>. Designing the non-atomic versions of the pointer functions to be thread-safe would be overkill if used in a single-threaded scenario. They would have a performance penalty.<\/li>\n<\/ul>\n<p>The correctness argument is probably the most important one. Why? The answer lies in the proposal. The proposal presents a thread-safe singly linked list that supports insertion, deletion, and searching of elements. This singly linked list is implemented in a lock-free way.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\" size-full wp-image-6077\" src=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/AtomicSinglyLinkedList.png\" alt=\"AtomicSinglyLinkedList\" style=\"display: block; margin-left: auto; margin-right: auto;\" width=\"515\" height=\"613\" srcset=\"https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/AtomicSinglyLinkedList.png 515w, https:\/\/www.modernescpp.com\/wp-content\/uploads\/2021\/01\/AtomicSinglyLinkedList-252x300.png 252w\" sizes=\"auto, (max-width: 515px) 100vw, 515px\" \/><\/p>\n<p>&nbsp;<\/p>\n<p>All changes required to compile the program with a C++11 compiler are marked in red. The implementation of atomic smart pointers is a lot easier and hence less error-prone. C++20&#8217;s type system does not permit it to use a non-atomic operation on an atomic smart pointer.<\/p>\n<p>Proposal <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2014\/n4162\">N4162<\/a> proposed the new types <code>std::atomic_shared_ptr<\/code> and <code>std::atomic_weak_ptr<\/code> as atomic smart pointers. By merging them in the mainline ISO C++ standard, they became partial template specialization of <a href=\"https:\/\/en.cppreference.com\/w\/cpp\/atomic\/atomic\">std::atomic<\/a>: <code>std::atomic&lt;std::shared_ptr&gt;<\/code>, and <code>std::atomic&lt;std::weak_ptr&gt;<\/code>.<\/p>\n<p>Consequently, the atomic operations for <code>std::shared_ptr&lt;T&gt;<\/code> are deprecated with C++20.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>With C++20, threads can be cooperatively interrupted.&nbsp; Let me show you in my <a href=\"https:\/\/www.modernescpp.com\/index.php\/cooperative-interruption-of-a-thread-in-c-20\">next,<\/a> what that means.<\/p>\n<p>&nbsp;<\/p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>In my last post, I introduced latches in C++20. A latch enables its threads to wait until a counter becomes zero. Additionally to a latch, its big sibling barrier can be used more than once. Today, I write about barriers and present atomic smart pointers.<\/p>\n","protected":false},"author":21,"featured_media":5199,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[375],"tags":[434,449,399],"class_list":["post-6078","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-c-20","tag-atomics","tag-barriers","tag-smart-pointers"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6078","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=6078"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6078\/revisions"}],"predecessor-version":[{"id":6715,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/6078\/revisions\/6715"}],"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=6078"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=6078"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=6078"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}