{"id":5196,"date":"2017-02-20T17:14:20","date_gmt":"2017-02-20T17:14:20","guid":{"rendered":"https:\/\/www.modernescpp.com\/index.php\/atomic-smart-pointers\/"},"modified":"2023-06-26T12:22:17","modified_gmt":"2023-06-26T12:22:17","slug":"atomic-smart-pointers","status":"publish","type":"post","link":"https:\/\/www.modernescpp.com\/index.php\/atomic-smart-pointers\/","title":{"rendered":"Atomic Smart Pointers"},"content":{"rendered":"<p>C++20 will have atomic smart pointers. To be exact, we will get a <span style=\"font-family: courier new,courier;\">std::atomic_shared_ptr<\/span> and a <span style=\"font-family: courier new,courier;\">std::atomic_weak_ptr<\/span>. But why? <span style=\"font-family: courier new,courier;\">std::shared_pt<\/span>r and <span style=\"font-family: courier new,courier;\">std::weak_ptr<\/span> are already thread-safe. Sort of. Let me dive into the details.<\/p>\n<p><!--more--><\/p>\n<p>&nbsp;<\/p>\n<p>Before I start, I want to make a short detour. This detour should only emphasize how important it is that the <span style=\"font-family: courier        new,courier;\">std::shared_ptr<\/span> has well-defined multithreading semantics, and you know and use it. From the multithreading point of view, <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> is this kind of data structure you will not use in multithreading programs. They are, by definition, shared and mutable; therefore they are the ideal candidates for data races and hence for undefined behavior. On the other hand, there is a guideline in modern C++: Don&#8217;t touch memory. That means, using smart pointers in multithreading programs. &nbsp;<\/p>\n<\/p>\n<h2>Half thread-safe<\/h2>\n<p>I often have the question in my C++ seminars: Are smart pointers thread-safe? My defined answer is yes and no. Why? A <span style=\"font-family: Courier New,Courier,monospace;\"> std::shared_ptr<\/span> consists of a control block and its resource. Yes, the control block is thread-safe; but no, the access to the resource is not thread-safe. That means, modifying the reference counter is an atomic operation and you have the guarantee that the resource will be deleted exactly once. These are all guarantees a <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> gives you.<\/p>\n<p>The assertion that a <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> provides is described by <a href=\"http:\/\/www.boost.org\/doc\/libs\/1_57_0\/libs\/smart_ptr\/shared_ptr.htm#ThreadSafety\">Boost.<\/a><\/p>\n<ol>\n<li>A<span style=\"font-family: courier new,courier;\"> shared_pt<\/span>r instance can be &#8220;read&#8221; (accessed using only const operations) simultaneously by multiple threads.<\/li>\n<li>Different<span style=\"font-family: courier new,courier;\"> shared_ptr<\/span> instances can be &#8220;written to&#8221; (accessed using mutable operations such as operator= or reset) simultaneously by multiple threads (even when these instances are copies and share the same reference count underneath.)<\/li>\n<\/ol>\n<p>To make the two statements clear, let me show a simple example. When you copy a <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> in a thread, all is fine.<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::shared_ptr&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; ptr = std::make_shared&lt;<span style=\"color: #2b91af;\">int<\/span>&gt;(2011);\r\n\r\n<span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #0000ff;\">auto<\/span> i= 0; i&lt;10; i++){\r\n   std::<span style=\"color: #0000ff;\">thread<\/span>([ptr]{                           (1)\r\n     std::shared_ptr&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; localPtr(ptr);        (2)\r\n     localPtr= std::make_shared&lt;int&gt;(2014);     (3)\r\n    }).detach(); \r\n}\r\n<\/pre>\n<\/div>\n<p>At first to (2). By using copy construction for the <span style=\"font-family: courier new,courier;\">std::shared_ptr localPtr<\/span>, only the control block is used. That is thread-safe. (3) is a little bit more interesting.&nbsp; <span style=\"font-family: courier new,courier;\">localPtr<\/span>&nbsp; (3)<span style=\"font-family: courier new,courier;\"> <\/span>is set to a new&nbsp; <span style=\"font-family: courier new,courier;\">std::shared_ptr.<\/span> This is from the multithreading point of view, no problem: Die lambda-function<span style=\"font-family: courier new,courier;\"> <\/span>(1) binds <span style=\"font-family: courier new,courier;\">ptr<\/span> by copy. Therefore, the modification of <span style=\"font-family: courier        new,courier;\">localPtr<\/span> takes place on a copy.<span style=\"font-family: courier new,courier;\"><\/span><\/p>\n<p>The story will change dramatically if I take the <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> by reference.<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::shared_ptr&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; ptr = std::make_shared&lt;<span style=\"color: #2b91af;\">int<\/span>&gt;(2011);  \r\n<span style=\"color: #2b91af;\"><\/span><span style=\"color: #2b91af;\"><\/span>\r\n<span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #0000ff;\">auto<\/span> i= 0; i&lt;10; i++){\r\n   std::<span style=\"color: #0000ff;\">thread<\/span>([&amp;ptr]{                         (1)\r\n     ptr= std::make_shared&lt;<span style=\"color: #2b91af;\">int<\/span>&gt;(2014);         (2)\r\n   }).detach(); \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The lambda function binds the <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> <span style=\"font-family: courier new,courier;\">ptr<\/span> by reference (1). Therefore assignment (2) is a race condition on the resource, and the program has undefined behavior.<\/p>\n<p>Admittedly that was not so easy to get. <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> requires special attention in a multithreading environment. They are very special. They are the only non-atomic data types in C+ for which atomic operations exist.<\/p>\n<h2>Atomic Operations for std::shared_ptr<\/h2>\n<p>There are specializations for the atomic operations <span style=\"font-family: courier new,courier;\">load,<\/span> <span style=\"font-family: courier new,courier;\">store,<\/span> <span style=\"font-family: courier new,courier;\">compare<\/span> and <span style=\"font-family: courier new,courier;\">exchange<\/span> for a <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span>.&nbsp; By using the <span style=\"font-family: courier new,courier;\">explicit<\/span> variant, you can even specify the memory model.<span style=\"font-family: courier new,courier;\"><\/span> Here are the free atomic operations for <span style=\"font-family: courier        new,courier;\">std::shared_ptr.<\/span><\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::atomic_is_lock_free(std::shared_ptr)\r\nstd::atomic_load(std::shared_ptr)\r\nstd::atomic_load_explicit(std::shared_ptr)\r\nstd::atomic_store(std::shared_ptr)\r\nstd::atomic_store_explicit(std::shared_ptr)\r\nstd::atomic_exchange(std::shared_ptr)\r\nstd::atomic_exchange_explicit(std::shared_ptr)\r\nstd::atomic_compare_exchange_weak(std::shared_ptr)\r\nstd::atomic_compare_exchange_strong(std::shared_ptr)\r\nstd::atomic_compare_exchange_weak_explicit(std::shared_ptr)\r\nstd::atomic_compare_exchange_strong_explicit(std::shared_ptr)\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>For the details, have a look at <a href=\"http:\/\/en.cppreference.com\/w\/cpp\/memory\/shared_ptr\">cppreference.com.<\/a> Now it is quite easy to modify a by reference bounded <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> in a thread-safe way.<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0; line-height: 125%;\">std::shared_ptr&lt;<span style=\"color: #2b91af;\">int<\/span>&gt; ptr = std::make_shared&lt;<span style=\"color: #2b91af;\">int<\/span>&gt;(2011);\r\n\r\n<span style=\"color: #0000ff;\">for<\/span> (<span style=\"color: #0000ff;\">auto<\/span> i =0;i&lt;10;i++){\r\n   std::<span style=\"color: #0000ff;\">thread<\/span>([&amp;ptr]{ \r\n     <span style=\"color: #0000ff;\">auto<\/span> localPtr= std::make_shared&lt;<span style=\"color: #2b91af;\">int<\/span>&gt;(2014);\r\n     std::atomic_store(&amp;ptr, localPtr);            (1)\r\n   }).detach(); \r\n}\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>The update of the <span style=\"font-family: courier new,courier;\">std::shared_ptr ptr<\/span> (1) is thread-safe. All is well? <strong>NO<\/strong>. Finally, we come to the new atomic smart pointers.<\/p>\n<h2>Atomic smart pointers<\/h2>\n<p><span class=\"vote-count-post\" itemprop=\"upvoteCount\">The proposal <\/span><a href=\"http:\/\/wg21.link\/n4162\" rel=\"nofollow\">N4162<\/a> for atomic smart pointers directly addresses the deficiencies of the current implementation. The deficiencies boil down to three points consistency, correctness, and performance. Here is an overview of the three points. For the details, you have to read the proposal.<\/p>\n<p><strong>Consistency:&nbsp;<\/strong> The atomic operations for the <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> are the only ones for a non-atomic data type.<\/p>\n<p><strong>Correctness:<\/strong> Using free atomic operations is error-prone because the right usage is based on discipline. It&#8217;s quite easy to forget to use an atomic operation &#8211; such as in the last example: I use <span style=\"font-family: courier new,courier;\">prt= localPtr<\/span> instead of <span style=\"font-family: courier new,courier;\">std::atomic_store(&amp;ptr, localPtr)<\/span>. The result is undefined behavior because of a data race. If we have instead used an atomic smart pointer, the compiler will not allow it.&nbsp;<\/p>\n<p><strong>Performance:<\/strong> The <span style=\"font-family: courier        new,courier;\">std::atomic_shared_ptr<\/span> and <span style=\"font-family: courier new,courier;\">std::atomic_weak_ptr<\/span> have a significant advantage over the free <span style=\"font-family: courier        new,courier;\">atomic_*<\/span> functions. They are designed for the particular use case multithreading and can have a <span style=\"font-family: courier new,courier;\">std::atomic_flag<\/span> as a kind of cheap <a href=\"https:\/\/www.modernescpp.com\/index.php\/the-atomic-flag\">Spinlock<\/a>. (You can read the details about s<span style=\"font-family: courier new,courier;\"><span id=\"transmark\"> <\/span><\/span>spinlocks and <span style=\"font-family: courier new,courier;\">std::atomic_flag<\/span> in the post <a href=\"https:\/\/www.modernescpp.com\/index.php\/the-atomic-flag\">The Atomic Flag<\/a>). It makes of course, not so much sense to put for a possible multithreading use cases an <span style=\"font-family: courier new,courier;\">std::atomic_flag<\/span> in each <span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> or <span style=\"font-family: courier new,courier;\">std::weak_ptr<\/span> to make them thread-safe.<span style=\"font-family: courier new,courier;\"> <\/span>But that would be the consequence if both have a spinlock for the multithreading use case, and we would have no atomic smart pointers. That means&nbsp;<span style=\"font-family: courier new,courier;\">std::shared_ptr<\/span> and <span style=\"font-family: courier new,courier;\">std::weak_ptr<\/span> would have been optimized for the particular use case.<\/p>\n<p>For me, the correct argument is 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 finding of elements. This singly-linked list is implemented in a lock-free way.<\/p>\n<h3>A thread-safe singly-linked list<\/h3>\n<p>&nbsp;<\/p>\n<div style=\"background: #ffffff; overflow: auto; width: auto; gray;border-width: .1em .1em .1em .8em;\">\n<pre style=\"margin: 0px; line-height: 125%;\"><span style=\"color: #0000ff;\">template<\/span>&lt;<span style=\"color: #0000ff;\">typename<\/span> T&gt; <span style=\"color: #0000ff;\">class<\/span> <span style=\"color: #2b91af;\">concurrent_stack<\/span> {\r\n    <span style=\"color: #0000ff;\">struct<\/span> Node { T t; shared_ptr&lt;Node&gt; next; };\r\n    <span style=\"color: #ff0000;\">atomic_<\/span>shared_ptr&lt;Node&gt; head;\r\n          <span style=\"color: #ff0000;\">\/\/ in C++11: remove \u201catomic_\u201d and remember to use the special\r\n          \/\/ functions every time you touch the variable<\/span>\r\n    concurrent_stack( concurrent_stack &amp;) =<span style=\"color: #0000ff;\">delete<\/span>;\r\n    <span style=\"color: #2b91af;\">void<\/span> <span style=\"color: #0000ff;\">operator<\/span>=(concurrent_stack&amp;) =<span style=\"color: #0000ff;\">delete<\/span>;\r\n\r\npublic:\r\n    concurrent_stack() =<span style=\"color: #0000ff;\">default<\/span>;\r\n    ~concurrent_stack() =<span style=\"color: #0000ff;\">default<\/span>;\r\n    <span style=\"color: #0000ff;\">class<\/span> <span style=\"color: #2b91af;\">reference<\/span> {\r\n        shared_ptr&lt;Node&gt; p;\r\n    public:\r\n       reference(shared_ptr&lt;Node&gt; p_) : p{p_} { }\r\n       T&amp; <span style=\"color: #0000ff;\">operator<\/span>* () { <span style=\"color: #0000ff;\">return<\/span> p-&gt;t; }\r\n       T* <span style=\"color: #0000ff;\">operator<\/span>-&gt;() { <span style=\"color: #0000ff;\">return<\/span> &amp;p-&gt;t; }\r\n    };\r\n\r\n    <span style=\"color: #0000ff;\">auto<\/span> find( T t ) <span style=\"color: #0000ff;\">const<\/span> {\r\n        <span style=\"color: #0000ff;\">auto<\/span> p = head.load(); <span style=\"color: #ff0000;\"> \/\/ in C++11: atomic_load(&amp;head)<\/span>\r\n        <span style=\"color: #0000ff;\">while<\/span>( p &amp;&amp; p-&gt;t != t )\r\n            p = p-&gt;next;\r\n        <span style=\"color: #0000ff;\">return<\/span> reference(move(p));\r\n    }\r\n    <span style=\"color: #0000ff;\">auto<\/span> front() <span style=\"color: #0000ff;\">const<\/span> {\r\n       <span style=\"color: #0000ff;\">return<\/span> reference(head); <span style=\"color: #ff0000;\">\/\/ in C++11: atomic_load(&amp;head)<\/span>\r\n    }\r\n    <span style=\"color: #2b91af;\">void<\/span> push_front( T t ) {\r\n      <span style=\"color: #0000ff;\">auto<\/span> p = make_shared&lt;Node&gt;();\r\n      p-&gt;t = t;\r\n      p-&gt;next = head;         <span style=\"color: #ff0000;\">\/\/ in C++11: atomic_load(&amp;head)<\/span>\r\n      <span style=\"color: #0000ff;\">while<\/span>( !head.compare_exchange_weak(p-&gt;next, p) ){ }\r\n      <span style=\"color: #ff0000;\">\/\/ in C++11: atomic_compare_exchange_weak(&amp;head, &amp;p-&gt;next, p);<\/span>\r\n    }\r\n    <span style=\"color: #2b91af;\">void<\/span> pop_front() {\r\n       <span style=\"color: #0000ff;\">auto<\/span> p = head.load();\r\n       <span style=\"color: #0000ff;\">while<\/span>( p &amp;&amp; !head.compare_exchange_weak(p, p-&gt;next) ){ }\r\n      <span style=\"color: #ff0000;\"> \/\/ in C++11: atomic_compare_exchange_weak(&amp;head, &amp;p, p-&gt;next);<\/span>\r\n    }\r\n};\r\n<\/pre>\n<\/div>\n<p>&nbsp;<\/p>\n<p>All changes necessary to compile the program with a C++11 compiler are in red. The implementation of atomic smart pointers is a lot easier and hence less error-prone. C++20 does not permit it to use a non-atomic operation on a <span style=\"font-family: courier new,courier;\">std::atomic_shared_ptr<\/span>.<\/p>\n<h2>What&#8217;s next?<\/h2>\n<p>C++11 got tasks like promises and futures an advanced multithreading concept. Although they offer a lot more threads, they have a significant shortcoming. C++11 futures can not be composed. Extended futures in C++20 will overcome this shortcoming. How? Read the <a href=\"https:\/\/www.modernescpp.com\/index.php\/std-future-extensions\">next post<\/a>.<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>C++20 will have atomic smart pointers. To be exact, we will get a std::atomic_shared_ptr and a std::atomic_weak_ptr. But why? std::shared_ptr and std::weak_ptr are already thread-safe. Sort of. Let me dive into the details.<\/p>\n","protected":false},"author":21,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[367],"tags":[434,399],"class_list":["post-5196","post","type-post","status-publish","format-standard","hentry","category-multithreading-c-17-and-c-20","tag-atomics","tag-smart-pointers"],"_links":{"self":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5196","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=5196"}],"version-history":[{"count":1,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5196\/revisions"}],"predecessor-version":[{"id":6885,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/posts\/5196\/revisions\/6885"}],"wp:attachment":[{"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/media?parent=5196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/categories?post=5196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.modernescpp.com\/index.php\/wp-json\/wp\/v2\/tags?post=5196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}