{"id":109563,"date":"2024-03-22T07:00:00","date_gmt":"2024-03-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109563"},"modified":"2024-04-06T09:31:24","modified_gmt":"2024-04-06T16:31:24","slug":"20240322-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240322-00\/?p=109563","title":{"rendered":"Why does my thread get a broken string as its initial parameter?"},"content":{"rendered":"<p>A customer passed a string to a newly-created <code>std::<wbr \/>thread<\/code> but found that the <code>std::<wbr \/>string<\/code> received by the thread was already invalid. How can that happen?<\/p>\n<p>Here was how they passed the parameter to the thread:<\/p>\n<pre>void OnWidgetChanged(const char* reason)\r\n{\r\n    std::thread backgroundThread(ProcessWidgetChange,\r\n        reason);\r\n    backgroundThread.detach();\r\n}\r\n\r\nvoid ProcessWidgetChange(std::string reason)\r\n{\r\n    \/\/ the reason is corrupted!\r\n}\r\n<\/pre>\n<p>The problem is that the raw <code>const char*<\/code> pointer is being converted to a <code>std::<wbr \/>string<\/code> too late.<\/p>\n<p>To avoid confusion, let me rename the two <code>reason<\/code> parameters:<\/p>\n<pre>void OnWidgetChanged(const char* <span style=\"border: solid 1px currentcolor;\">rawReason<\/span>)\r\n{\r\n    std::thread backgroundThread(ProcessWidgetChange,\r\n        <span style=\"border: solid 1px currentcolor;\">rawReason<\/span>);\r\n    backgroundThread.detach();\r\n}\r\n\r\nvoid ProcessWidgetChange(std::string <span style=\"border: solid 1px currentcolor;\">stringReason<\/span>)\r\n{\r\n    \/\/ the <span style=\"border: solid 1px currentcolor;\">stringReason<\/span> is corrupted!\r\n}\r\n<\/pre>\n<p>The parameters to the <code>std::<wbr \/>thread<\/code> constructor are captured by value, and the copies are then passed to the new thread, which executes the desired code by passing those copies to <code>std::invoke<\/code>. The specification calls out that the capturing happens <a href=\"https:\/\/timsong-cpp.github.io\/cppwp\/n4868\/thread.thread.constr\"> in the calling thread as part of the constructor<\/a>.<\/p>\n<p>Here&#8217;s a flowchart of the operations that take place:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentcolor; border-style: none solid solid none;\">Original thread<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Capture parameters by value<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Start new thread<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor; text-align: right;\">\u2933<\/td>\n<td style=\"border: 1px currentcolor; border-style: none none solid solid;\">New thread<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Convert parameters to match signature of target<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Call the target<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Destruct converted parameters<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the above example, it means that the raw <code>const char*<\/code> is captured and given to the thread. Only when the thread begins execution is the raw <code>const char*<\/code> converted to a <code>std::<wbr \/>string<\/code>, and by that point, it&#8217;s too late.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentcolor; border-style: none solid solid none;\">Original thread<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Copy the <code>rawReason<\/code> raw pointer<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Start new thread<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor; text-align: right;\">\u2933<\/td>\n<td style=\"border: 1px currentcolor; border-style: none none solid solid;\">New thread<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Convert: Construct <code>stringReason<\/code> from <code>rawReason<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Call the target with <code>stringReason<\/code><\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Destruct: Destruct <code>stringReason<\/code> and <code>rawReason<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>The problem is clearer when the conversion is spelled out: The original code has already detached the thread and returned, at which point the caller of <code>OnWidgetChanged<\/code> is not under any obligation to keep the pointer valid any further. The conversion to a <code>std::string<\/code> therefore happens too late.<\/p>\n<p>The author of the above code was under the incorrect belief that the operations occurred in a different order:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td style=\"border: 1px currentcolor; border-style: none solid solid none;\">Original thread<\/td>\n<td>&nbsp;<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Convert: Construct <code>stringReason<\/code> from <code>rawReason<\/code><\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Capture converted parameters<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">Start new thread<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">\u00a0<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor; text-align: right;\">\u2933<\/td>\n<td style=\"border: 1px currentcolor; border-style: none none solid solid;\">New thread<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Call the target<\/td>\n<\/tr>\n<tr>\n<td style=\"border-right: 1px solid currentcolor;\">\u00a0<\/td>\n<td style=\"border-left: 1px solid currentcolor;\">Destruct: Destruct <code>stringReason<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>If you think about it, it&#8217;s not possible to do things the second way. The <code>thread<\/code> constructor could try to infer the signature of the callable, but the callable might have multiple <code>operator()<\/code> overloads, so the <a title=\"Deconstructing function pointers in a C++ template\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200713-00\/?p=103978\"> tricks for deconstructing the function pointer<\/a> don&#8217;t work. And even if they did work, it&#8217;s possible that the caller didn&#8217;t pass the same number of parameters as the function expects, but expecting it to work because the missing parameters have default values. But there&#8217;s no way to deduce the default values (because default values are not part of the function type signature), so the converter doesn&#8217;t know what values to use for the missing parameters.<\/p>\n<p>The solution is to convert the <code>rawReason<\/code> raw pointer to a <code>std::<wbr \/>string<\/code> while the <code>rawReason<\/code> is still valid, pass that converted value to the <code>std::thread<\/code> constructor.<\/p>\n<pre>void OnWidgetChanged(const char* rawReason)\r\n{\r\n    std::thread backgroundThread(ProcessWidgetChange,\r\n        <span style=\"border: solid 1px currentcolor;\">std::string(rawReason)<\/span>);\r\n    backgroundThread.detach();\r\n}\r\n\r\nvoid ProcessWidgetChange(std::string stringReason)\r\n{\r\n    \/\/ use the stringReason\r\n}\r\n<\/pre>\n<p>In the original customer version, the issue wasn&#8217;t about an anticipated conversion from a raw pointer to a <code>std::<wbr \/>string<\/code>, but rather an anticipated conversion of a raw COM pointer to a smart one:<\/p>\n<pre>void OnWidgetChanged(IWidget* widget)\r\n{\r\n    \/\/ Oops, captures raw pointer, not ComPtr.\r\n    std::thread backgroundThread(ProcessWidgetChange,\r\n        widget);\r\n    backgroundThread.detach();\r\n}\r\n\r\nvoid ProcessWidgetChange(ComPtr&lt;IWidget&gt; widget)\r\n{\r\n    widget-&gt;Refresh();\r\n}\r\n<\/pre>\n<p>The solution, as before, is to force the conversion early:<\/p>\n<pre>void OnWidgetChanged(IWidget* widget)\r\n{\r\n    std::thread backgroundThread(&amp;ProcessWidgetChange,\r\n        <span style=\"border: solid 1px currentcolor;\">ComPtr&lt;IWidget&gt;(widget)<\/span>);\r\n    backgroundThread.detach();\r\n}\r\n<\/pre>\n<p><b>Bonus chatter<\/b>: It would have been nice to use Class Template Argument Deduction (CTAD) to simplify this to<\/p>\n<pre>void OnWidgetChanged(IWidget* widget)\r\n{\r\n    std::thread backgroundThread(&amp;ProcessWidgetChange,\r\n        <span style=\"border: solid 1px currentcolor;\">ComPtr(widget)<\/span>);\r\n    backgroundThread.detach();\r\n}\r\n<\/pre>\n<p>Unfortunately, as we saw earlier, <a title=\"How well does WRL ComPtr support class template argument deduction (CTAD)?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240315-00\/?p=109537\"> WRL&#8217;s <code>ComPtr<\/code> doesn&#8217;t support CTAD<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It got converted too late.<\/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-109563","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It got converted too late.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109563","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=109563"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109563\/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=109563"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109563"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109563"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}