{"id":107727,"date":"2023-01-19T07:00:00","date_gmt":"2023-01-19T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107727"},"modified":"2023-09-19T18:05:43","modified_gmt":"2023-09-20T01:05:43","slug":"20230119-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230119-00\/?p=107727","title":{"rendered":"Windows Runtime asynchronous operations can fail in two different ways, so make sure you get them both"},"content":{"rendered":"<p>CLR Tasks, PPL tasks, JavaScript Promises, and Windows Runtime asynchronous actions and operations can fail in two ways.<\/p>\n<ul>\n<li>They can throw an exception instead of returning the <code>Task<\/code>, <code>task<\/code>, <code>IAsyncAction<\/code>, or <code>IAsyncOperation<\/code>. &#8220;Synchronous failure.&#8221;<\/li>\n<li>They can return a <code>Task<\/code>, <code>task<\/code>, <code>IAsyncAction<\/code>, or <code>IAsyncOperation<\/code> which completes with an exception. &#8220;Asynchronous failure.&#8221;<\/li>\n<\/ul>\n<p>Synchronous failures are raised at the point you call the method; you can think of them as &#8220;immediate failure&#8221;. Asynchronous failure are raised at the point you check the result; you can think of them as &#8220;delayed failure&#8221;.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>Framework<\/th>\n<th>Synchronous failure<\/th>\n<th>Asynchronous failure<\/th>\n<\/tr>\n<tr>\n<td>C#<\/td>\n<td><code>var task = o.DoSomethingAsync()<\/code><\/td>\n<td><code>task.Result<\/code><br \/>\n<code><code>await task<\/code><\/code><\/td>\n<\/tr>\n<tr>\n<td>PPL<\/td>\n<td><code>auto task = o-&gt;DoSomethingAsync()<\/code><\/td>\n<td><code>task.get()<\/code><br \/>\n<code>co_await task<\/code><\/td>\n<\/tr>\n<tr>\n<td>C++\/WinRT<\/td>\n<td><code>auto op = o.DoSomethingAsync()<\/code><\/td>\n<td><code>op.GetResults()<\/code><br \/>\n<code>co_await op<\/code><\/td>\n<\/tr>\n<tr>\n<td>JavaScript<\/td>\n<td><code>var p = o.DoSomethingAsync()<\/code><\/td>\n<td><code>p.catch()<\/code><br \/>\n<code>await p<\/code><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>A customer reported that they were getting exceptions from some code, which they couldn&#8217;t understand because they thought they were handling exceptions.<\/p>\n<pre>\/\/ C++ with PPL\r\nusing namespace Concurrency;\r\nusing namespace Platform;\r\n\r\ntask&lt;String^&gt;\r\nWidget::GetNameAsync()\r\n{\r\n    return m_doodad-&gt;GetNameAsync() \/\/ crash here\r\n    .then([](task&lt;String^&gt; outerTask) {\r\n        String^ name;\r\n\r\n        try {\r\n            name = outerTask.get();\r\n        } catch (...) {\r\n        }\r\n\r\n        return name;\r\n    }, task_continuation_context::use_arbitrary());\r\n}\r\n<\/pre>\n<p>The code wraps the <code>outerTask.get()<\/code> inside a <code>try<\/code> block, so that should catch all the exceptions that come out of the <code>m_doodad-&gt;GetNameAsync()<\/code> task.<\/p>\n<p>And that&#8217;s true, it does catch all the exceptions that come out of the task.<\/p>\n<p>But the exception that crashed didn&#8217;t come out of the task!<\/p>\n<p>The debugger pointed at the line that raised the exception: It was from the call to <code>GetNameAsync()<\/code> itself, before it even returned a task. The customer got so focused on the Concurrency Runtime that they forgot about the basic rules of C++: If you want to catch an exception, you have to do it inside a <code>try<\/code> block.<\/p>\n<p>In order to catch that exception, the call to <code>GetNameAsync()<\/code> must itself be inside a <code>try<\/code> block.<\/p>\n<pre>task&lt;String^&gt;\r\nWidget::GetNameAsync()\r\n{\r\n    task&lt;String^&gt; nameTask;\r\n    <span style=\"border: solid 1px currentcolor;\">try {<\/span>\r\n        nameTask = m_doodad-&gt;GetNameAsync();\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">} catch (...) {                               <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return task_from_result&lt;String^&gt;(nullptr);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                             <\/span>\r\n\r\n    return nameTask.then([](task&lt;String^&gt; outerTask) {\r\n        String^ name;\r\n\r\n        try {\r\n            name = outerTask.get();\r\n        } catch (...) {\r\n        }\r\n\r\n        return name;\r\n    }, task_continuation_context::use_arbitrary());\r\n}\r\n<\/pre>\n<p>I separated the <code>return m_doodad-&gt;GetNameAsync().then()<\/code> into two steps:<\/p>\n<pre>    task&lt;String^&gt; nameTask = m_doodad-&gt;GetNameAsync();\r\n    return nameTask.then(...);\r\n<\/pre>\n<p>The <code>try<\/code> statement inside the <code>then<\/code> lambda deals with exceptions that come out of the task. We just need another <code>try<\/code> to deal with the exceptions that occur while trying to produce the task:<\/p>\n<pre>    task&lt;String^&gt; nameTask;\r\n    <span style=\"border: solid 1px currentcolor;\">try {<\/span>\r\n        nameTask = m_doodad-&gt;GetNameAsync();\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">} catch (...) {                               <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return task_from_result&lt;String^&gt;(nullptr);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                             <\/span>\r\n<\/pre>\n<p>If an exception occurs, we catch it and return an already-completed task that produces an empty string. Otherwise, we hook up the continuation that deals with the task completion as before.<\/p>\n<p>Once you see how the expression was taken apart, you can combine them again, putting the entire statement inside a giant <code>try<\/code> block, even though it&#8217;s only the <code>-&gt;GetNameAsync()<\/code> that we&#8217;re interested in. (Most languages with exceptions make it cumbersome to catch exceptions that come out of part of an expression, so most people just expand the scope of the <code>try<\/code> to include the entire statement.)<\/p>\n<pre>task&lt;String^&gt;\r\nWidget::GetNameAsync()\r\n{\r\n    <span style=\"border: solid 1px currentcolor;\">try {<\/span>\r\n        return m_doodad-&gt;GetNameAsync()\r\n        .then([](task&lt;String^&gt; outerTask) {\r\n            String^ name;\r\n\r\n            try {\r\n                name = outerTask.get();\r\n            } catch (...) {\r\n            }\r\n\r\n            return name;\r\n        }, task_continuation_context::use_arbitrary());\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">} catch (...) {                               <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    return task_from_result&lt;String^&gt;(nullptr);<\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">}                                             <\/span>\r\n}\r\n<\/pre>\n<p>Note that if the customer had been using PPL with <code>co_await<\/code> support, the <code>try<\/code> block would naturally have enclosed both the production of the task as well as handling for its completion: The inability to wrap just part of an expression in a <code>try<\/code> block actually helps you write correct code this time:<\/p>\n<pre>task&lt;String^&gt;\r\nWidget::GetNameAsync()\r\n{\r\n    try {\r\n        co_return co_await m_doodad-&gt;GetNameAsync();\r\n    } catch (...) {\r\n        co_return nullptr;\r\n    }\r\n}\r\n<\/pre>\n<p>One catch with this rewrite is that <code>co_await<\/code> of a Concurrency Runtime <code>task<\/code> does not let you control the task continuation context. It always uses <code>get_current_winrt_context()<\/code> when awaiting tasks, and <code>CallbackContext::Same<\/code> when awaiting Windows Runtime asynchronous actions and operations.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Fail me now or fail me later.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-107727","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Fail me now or fail me later.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107727","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=107727"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107727\/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=107727"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107727"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107727"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}