{"id":106182,"date":"2022-01-21T07:00:20","date_gmt":"2022-01-21T15:00:20","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106182"},"modified":"2022-01-21T06:30:34","modified_gmt":"2022-01-21T14:30:34","slug":"fixing-the-crash-that-seems-to-be-on-a-stdmove-operation","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220121-20\/?p=106182","title":{"rendered":"Fixing the crash that seems to be on a <CODE>std::move<\/CODE> operation"},"content":{"rendered":"<p>Last time, we looked at a crash that was root-caused to <a title=\"The mystery of the crash that seems to be on a std::move operation\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220120-00\/?p=106178\"> an order of evaluation bug if compiled as C++14<\/a>. One solution to the problem is to switch to C++17 mode, but presumably the customer isn&#8217;t willing to make that drastic a change to their product yet. Maybe there&#8217;s something we can do that is less disruptive.<\/p>\n<pre>    \/\/ std::shared_ptr&lt;Test&gt; test;\r\n    <span style=\"border: solid 1px black;\">test<\/span>-&gt;harness-&gt;callAndReport([<span style=\"border: solid 1px black;\">test2 = std::move(test)<\/span>]() ...);\r\n<\/pre>\n<p>The problem here is that we move the pointer out of the <code>test<\/code> when building the lambda, while at the same time fetching the pointer from the <code>test<\/code> in order to locate the function to call. In C++14, these two operations are not sequenced, so there is no guarantee on the order of evaluation, even though our code depends on it:<\/p>\n<table class=\"cp3\" style=\"text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\"><code>test<\/code><\/td>\n<td style=\"text-align: left;\" colspan=\"2\">\u2190 need this to happen<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>harness<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>before this happens<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\"><code>test2 = std::move(test)<\/code><\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>callAndReport<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">lambda constructed<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"3\">function call<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>We&#8217;ll have to break the large expression (with unspecified order of evaluation) into two expressions that force the desired order of evaluation.<\/p>\n<p>We need the retrieval of the object referenced by <code>test<\/code> to happen before the evaluation of <code>test2 = std::move(test)<\/code>. So let&#8217;s write it out explicitly in the order we want the operations to happen, as two separate statements to enforce sequencing.<\/p>\n<p>There are a few ways of doing this, depending on where we want to break the chain in the above diagram.<\/p>\n<p>We could break it as soon as possible:<\/p>\n<pre>    <span style=\"color: blue;\">auto&amp; original_test = *test; \/\/ get this before we std::move(test)\r\n    original_test.<\/span>harness-&gt;callAndReport([test2 = std::move(test)]() ...);\r\n<\/pre>\n<p>An equivalent, perhaps less weird version, is this:<\/p>\n<pre>    <span style=\"color: blue;\">auto test_ptr = test.get(); \/\/ get this before we std::move(test)\r\n    test_ptr<\/span>-&gt;harness-&gt;callAndReport([test2 = std::move(test)]() ...);\r\n<\/pre>\n<p>Both of these force the sequencing of the &#8220;figure out what <code>test<\/code> points to&#8221; to happen before the rest of the expression:<\/p>\n<table class=\"cp3\" style=\"text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\"><code>test<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td colspan=\"3\">\n<hr \/>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>harness<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\"><code>test2 = std::move(test)<\/code><\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>callAndReport<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">lambda constructed<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"3\">function call<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>Perhaps even less weird-looking is sequencing after the acquisition of the <code>harness<\/code>.<\/p>\n<pre>    <span style=\"color: blue;\">auto&amp; harness = test-&gt;harness; \/\/ get this before we std::move(test)\r\n    harness<\/span>-&gt;callAndReport([test2 = std::move(test)]() ...);\r\n<\/pre>\n<p>This moves the explicit sequencing a little later in the evaluation of the left column:<\/p>\n<table class=\"cp3\" style=\"text-align: center;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: solid 1px black;\"><code>test<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>harness<\/code><\/td>\n<td>&nbsp;<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td colspan=\"3\">\n<hr \/>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>operator-&gt;<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\"><code>test2 = std::move(test)<\/code><\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\"><code>callAndReport<\/code><\/td>\n<td>&nbsp;<\/td>\n<td style=\"border: solid 1px black;\">lambda constructed<\/td>\n<\/tr>\n<tr>\n<td>\u2193<\/td>\n<td>&nbsp;<\/td>\n<td>\u2193<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" colspan=\"3\">function call<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>It doesn&#8217;t really matter where we put it, as long as it definitely sequences the read of <code>test<\/code> ahead of its modification by move-assignment.<\/p>\n<p>But maybe we&#8217;re trying too hard.<\/p>\n<p>The problem with the original code was that it was being too clever, using <code>std::move<\/code> to transfer the <code>std::shared_ptr<\/code> into the lambda and avoid bumping and then dropping the reference count. But what did we really save?<\/p>\n<p>Let&#8217;s look at the entire (repaired) function and evaluate its effect on the <code>std::shared_ptr<\/code>:<\/p>\n<pre>void polarity_test(std::shared_ptr&lt;Test&gt; test)\r\n{\r\n    auto&amp; harness = test-&gt;harness; \/\/ get this before we std::move(test)\r\n    harness-&gt;callAndReport([test2 = std::move(test)]() mutable\r\n    {\r\n        ...\r\n    });\r\n}\r\n<\/pre>\n<p>The <code>test<\/code> is destructed at the end of the function, and that&#8217;s unavoidable.<\/p>\n<p>The <code>test2<\/code> is copy-constructed from <code>test<\/code>, and it destructs when the lambda is destroyed.<\/p>\n<p>I checked gcc, clang, MSVC, and icc, and none of them optimize out the <code>test<\/code> destructor, not even in simple cases like this:<\/p>\n<pre>void simple(std::shared_ptr&lt;int&gt; p)\r\n{\r\n    p.reset();\r\n    \/\/ run the destructor now\r\n}\r\n<\/pre>\n<p>For gcc, clang, and icc, the reason is that the call site is responsible for destructing parameters, and the call site doesn&#8217;t know what the ultimate fate of the <code>shared_ptr<\/code> is. (That changes if the call is inlined, though.)<\/p>\n<p>So all you&#8217;re really saving by moving the <code>test<\/code> into the lambda is the increment of the reference count. It turns out incrementing the reference count of a <code>shared_ptr<\/code> isn&#8217;t that bad. It&#8217;s a null pointer test and an interlocked increment.<\/p>\n<p>This doesn&#8217;t appear to be a performance-sensitive code path. Indeed, it looks like a test! The simplest solution is probably just to avoid the optimization and copy the <code>shared_ptr<\/code>.<\/p>\n<pre>    test-&gt;harness-&gt;callAndReport([<span style=\"color: blue;\">test2 = test<\/span>]() ...);\r\n<\/pre>\n<p><b>Reminder<\/b>: C++17 strengthened the order of evaluation rules and now requires that for function calls, determining the function to call must be performed before the arguments are evaluated.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Getting the order of evaluation to be what you want.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-106182","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Getting the order of evaluation to be what you want.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106182","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=106182"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106182\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=106182"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106182"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106182"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}