{"id":109490,"date":"2024-03-07T07:00:00","date_gmt":"2024-03-07T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109490"},"modified":"2024-03-07T09:04:08","modified_gmt":"2024-03-07T17:04:08","slug":"20240307-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240307-00\/?p=109490","title":{"rendered":"In C++\/WinRT, you shouldn&#8217;t destroy an object while you&#8217;re <CODE>co_await<\/CODE>ing it"},"content":{"rendered":"<p>A customer ran into a problem where they were crashing inside a <code>co_await<\/code>. Here&#8217;s a minimal version:<\/p>\n<pre>struct MyThing : winrt::implements&lt;MyThing, winrt::IInspectable&gt;\r\n{\r\n    winrt::IAsyncAction m_pendingAction{ nullptr };\r\n\r\n    winrt::IAsyncAction DoSomethingAsync() {\r\n        auto lifetime = get_strong();\r\n        m_pendingAction = LongOperationAsync();\r\n        co_await m_pendingAction;\r\n        PostProcessing();\r\n    }\r\n\r\n    void Cancel() {\r\n        if (m_pendingAction) {\r\n            m_pendingAction.Cancel();\r\n            m_pendingAction = nullptr;\r\n        }\r\n    }\r\n};\r\n<\/pre>\n<p>With this class, you can ask it to <code>Do\u00adSomething\u00adAsync()<\/code>, and it will set into motion some long asynchronous operation, and then do some post-processing of the result. If you are impatient, you can call <code>MyThing::<wbr \/>Cancel()<\/code> to give up on that long operation. For simplicity, we&#8217;ll assume that all calls occur on the same thread.<\/p>\n<p>What the customer found was that after calling <code>MyThing::<wbr \/>Cancel()<\/code>, the <code>co_await<\/code> crashed on a null pointer.<\/p>\n<p>Some time ago, I set down some <a title=\"Basic ground rules for programming - function parameters and how they are used\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20060320-13\/?p=31853\"> basic ground rules for function parameters<\/a>, and one of them was that function parameters must remain stable for the duration of a function call.<\/p>\n<p>In this case, it&#8217;s not so much a function call as it is a function suspension, so the rule doesn&#8217;t apply exactly, but the spirit is the same. The <code>MyThing::<wbr \/>Cancel()<\/code> method cancels the <code>m_pendingAction<\/code>, which is fine, but it also modifies <code>m_pendingAction<\/code> out from under the <code>co_await<\/code> that is using it! When you call <code>m_pendingAction.<wbr \/>Cancel()<\/code>, that cancels the <code>LongOperationAsync()<\/code>, which completes the <code>IAsyncAction<\/code>. At this point, the <code>co_await<\/code> will resume and call <code>m_pendingAction.GetResults()<\/code> to rethrown any errors.<\/p>\n<p>But <code>m_pendingAction<\/code> was nulled out by the <code>MyThing::<wbr \/>Cancel()<\/code>, so it&#8217;s calling <code>GetResults()<\/code> on a null pointer, which leads to the null pointer crash.<\/p>\n<p>The solution here is not to <code>co_await<\/code> the member variable directly, but rather to make a copy and <code>co_await<\/code> the copy. One way to do that is to copy to a local and await the local.<\/p>\n<pre>    winrt::IAsyncAction DoSomethingAsync() {\r\n        auto lifetime = get_strong();\r\n        m_pendingAction = LongOperationAsync();\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Await a copy of m_pendingAction because Cancel() <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ modifies m_pendingAction.                        <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">auto pendingAction = m_pendingAction;               <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">co_await pendingAction;                             <\/span>\r\n        PostProcessing();\r\n    }\r\n<\/pre>\n<p>Another is to create a copy as an inline temporary by doing a conversion to itself.<\/p>\n<pre>    winrt::IAsyncAction DoSomethingAsync() {\r\n        auto lifetime = get_strong();\r\n        m_pendingAction = LongOperationAsync();\r\n        <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Await a copy of m_pendingAction because Cancel() <\/span>\r\n        <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ modifies m_pendingAction.                        <\/span>\r\n        <span style=\"border: solid 1px currentcolor; border-top: none;\">co_await winrt::IAsyncAction(m_pendingAction);      <\/span>\r\n        PostProcessing();\r\n    }\r\n<\/pre>\n<p>We&#8217;ll look some more at ways to force a copy next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A generalization of the ground rules of programming.<\/p>\n","protected":false},"author":1069,"featured_media":100998,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-109490","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>A generalization of the ground rules of programming.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109490","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=109490"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109490\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/100998"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=109490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}