{"id":109971,"date":"2024-07-15T07:00:00","date_gmt":"2024-07-15T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109971"},"modified":"2024-06-24T10:44:50","modified_gmt":"2024-06-24T17:44:50","slug":"20240715-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240715-00\/?p=109971","title":{"rendered":"Creating an already-completed asynchronous activity in C++\/WinRT, part 5"},"content":{"rendered":"<p>Last time, we <a title=\"Creating an already-completed asynchronous activity in C++\/WinRT, part 4\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109967\"> created a generalized <code>Make\u00adFailed<\/code> for creating an already-completed asynchronous activity in the failure state<\/a>. We left with a note that debuggability still needs to be investigated. So let&#8217;s investigate it.<\/p>\n<p>When the Watson post-mortem debugger captures a crash dump, one of the tools you can use to understand the source of the exception is the stowed exception information. This is information captured at the point the exception is raised, which is helpful because the exception may get caught and re-raised, and you want to see the place the originally threw the exception, not the place the did the final uncaught re-raise.<\/p>\n<p><a title=\"What's the difference between throwing a winrt::hresult_error and using winrt::throw_hresult?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210716-00\/?p=105448\"> There are two ways to throw an <code>hresult_<wbr \/>error<\/code> object<\/a>.<\/p>\n<ul>\n<li>One is to construct a <code>hresult_<wbr \/>error<\/code> normally and throw it. This captures a stack trace at the point of construction.<\/li>\n<li>The other way is to use <code>winrt::<wbr \/>throw_hresult<\/code>. This recaptures the track trace left behind by a previous error.<\/li>\n<\/ul>\n<p>We are originating the exception, so we want to construct a <code>hresult_<wbr \/>error<\/code> at the point we create the failed asynchronous activity, so that the post-mortem stack trace will show the stack that led up to our error. Now, in our case, the entire coroutine body runs synchronously since there are no suspending <code>co_await<\/code>s, so the difference doesn&#8217;t really matter much, aside from a little extra stack clutter in the post-mortem debugger. But if we wanted something like a delayed error:<\/p>\n<pre>template&lt;typename Async, typename Error,\r\n    typename = std::enable_if_t&lt;\r\n        std::is_base_of_v&lt;std::exception, Error&gt; ||\r\n        std::is_base_of_v&lt;winrt::hresult_error, Error&gt;&gt;&gt;\r\nAsync MakeDelayedFailed(Error error,\r\n                        winrt::Windows::Foundation::TimeSpan delay)\r\n{\r\n    (void) co_await winrt::resume_after(delay);\r\n    throw error;\r\n}\r\n<\/pre>\n<p>we want the debugging stack to tell us that the exception came from whoever called <code>Make\u00adDelayed\u00adFailed()<\/code>, rather than telling us that the exception came from the thread pool.<\/p>\n<p>This means that we were correct to accept exception object as a by-value parameter. That way, it has already been constructed and therefore has already captured a stack trace.<\/p>\n<p>There&#8217;s another case we haven&#8217;t dealt with, though: Propagating an exception into the failed action.<\/p>\n<pre>winrt::IAsyncAction SetNameAsync(winrt::hstring const&amp; name)\r\n{\r\n    try {\r\n        SetName(name);\r\n        return MakeCompletedAsyncAction();\r\n    } catch (...) {\r\n        return MakeFailed&lt;\r\n            winrt::Windows::Foundation::IAsyncAction&gt;\r\n            (\u27e6???\u27e7);\r\n    }\r\n}\r\n<\/pre>\n<p>We want to create a failed <code>IAsyncAction<\/code> that contains whatever exception was thrown by <code>SetName<\/code>, but how do you pass the caught <code>...<\/code> as a variable? <!-- backref: C++\/WinRT gotcha: Not all exceptions derive from hresult_error --> Not all C++\/WinRT exceptions derive from <code>hresult_<wbr \/>error<\/code>, so it&#8217;s not enough to just catch <code>winrt::<wbr \/>hresult_<wbr \/>error<\/code>.<\/p>\n<p>The C++ standard library has a <code>exception_<wbr \/>ptr<\/code> class which represents an arbitrary exception. This is what <code>std::<wbr \/>current_<wbr \/>exception()<\/code> returns, and you can throw whaever that <code>exception_<wbr \/>ptr<\/code> represents by calling <code>std::<wbr \/>rethrow_<wbr \/>exception()<\/code>. exception. So we can add an overload of <code>Make\u00adFailed\u00adAsync\u00adAction<\/code> that takes an <code>exception_ptr<\/code>:<\/p>\n<pre>template&lt;typename Async&gt;\r\nAsync MakeFailed(std::exception_ptr ptr)\r\n{\r\n    (void) co_await winrt::get_cancellation_token();\r\n    std::rethrow_exception(ptr);\r\n}\r\n<\/pre>\n<p>We can use this overload from a <code>catch(...)<\/code> clause:<\/p>\n<pre>winrt::IAsyncAction SetNameAsync(winrt::hstring const&amp; name)\r\n{\r\n    try {\r\n        SetName(name);\r\n        return MakeCompletedAsyncAction();\r\n    } catch (...) {\r\n        return MakeFailed&lt;\r\n            winrt::Windows::Foundation::IAsyncAction&gt;\r\n            (std::current_exception());\r\n    }\r\n}\r\n<\/pre>\n<p>This has the advantage of preserving the stack trace from the exception thrown by <code>Set\u00adName()<\/code>, so that your post-mortem debugging gets a stack trace that leads to whatever point inside <code>Set\u00adName()<\/code> triggered the exception, rather than just getting a stack trace that points to our <code>catch<\/code> clause.<\/p>\n<p>In fact, we can use the <code>std::<wbr \/>exception_<wbr \/>ptr<\/code> as our common currency for exceptions.<\/p>\n<pre>template&lt;typename Async, typename Error&gt;\r\nAsync MakeFailed(Error&amp;&amp; error)\r\n{\r\n    return MakeFailed&lt;Async&gt;(\r\n        std::make_exception_ptr(\r\n            std::forward&lt;Error&gt;(error)));\r\n}\r\n<\/pre>\n<p>Another problem is that it&#8217;s annoying having to write out the template type parameter all the time:<\/p>\n<pre>winrt::Windows::Foundation::IAsyncAction\r\n    SaveAsync()\r\n{\r\n    return MakeFailed&lt;\r\n        <span style=\"border: solid 1px currentcolor;\">winrt::Windows::Foundation::IAsyncAction<\/span>&gt;\r\n        (winrt::hresult_access_denied());\r\n}\r\n<\/pre>\n<p>We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Trying to fail more correctly.<\/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-109971","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Trying to fail more correctly.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109971","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=109971"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109971\/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=109971"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109971"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109971"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}