{"id":103207,"date":"2019-12-12T07:00:00","date_gmt":"2019-12-12T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103207"},"modified":"2019-12-11T23:04:54","modified_gmt":"2019-12-12T07:04:54","slug":"20191212-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191212-00\/?p=103207","title":{"rendered":"C++ coroutines: Awaiting an <CODE>IAsyncAction<\/CODE> without preserving thread context"},"content":{"rendered":"<p>The C++\/WinRT library provides an awaiter for Windows Runtime asynchronous activities. Those asynchronous activities are represented by <code>IAsync\u00adAction<\/code>, <code>IAsync\u00adOperation<\/code>, and progress versions of the above. The C++\/WinRT-provided awaiter resumes execution of the caller in the same COM apartment that awaited the activity.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191125-00\/?p=103135\"> Here&#8217;s a refresher on COM apartments<\/a>. If you don&#8217;t want to read it, then a simplified version is to say that it resumes execution in the same UI context. If you perform the <code>co_await<\/code> on a UI thread, then when the asynchronous activity completes, the caller resumes execution on the same UI thread. If you perform the <code>co_await<\/code> on a background thread, then the caller resumes execution on a background thread (possibly not the exact same thread that initiated the operation).<\/p>\n<p>But maybe you don&#8217;t need to resume in the same apartment. Your code is fine with running in any apartment, How can you <code>co_await<\/code> a Windows Runtime asynchronous activity and resume execution on any thread?<\/p>\n<p>With a custom awaiter, of course.<\/p>\n<pre>template&lt;typename Async&gt;\r\n[[nodiscard]] auto resume_in_any_apartment(Async async)\r\n{\r\n  struct awaiter : std::experimental::suspend_always\r\n  {\r\n    awaiter(Async async_) : async(std::move(async_)) { }\r\n\r\n    void await_suspend(\r\n        std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      async.Completed([handle](auto&amp;&amp;...) { handle(); });\r\n    }\r\n\r\n    auto await_resume()\r\n    {\r\n        return async.GetResults();\r\n    }\r\n    Async async;\r\n  };\r\n  return awaiter{ std::move(async) };\r\n}\r\n<\/pre>\n<p>Note that we use the function pattern for generating the awaiter because that makes it easier to generate a different awaiter for the four different kinds of Windows Runtime asynchronous activities: We can templatize the function and propagate the type into the custom awaiter. (Alternatively, we could use CTAD.)<\/p>\n<p>Our custom awaiter has a simple constructor that moves its parameter, and the <code>resume_<\/code><code>in_<\/code><code>any_<\/code><code>apartment<\/code> constructs the object by moving its own parameter into the awaiter. This moves the original parameter to the <code>resume_<\/code><code>in_<\/code><code>any_<\/code><code>apartment<\/code> function all the way into the awaiter.<\/p>\n<p>When the caller performs the <code>co_await<\/code> of this custom awaiter, we schedule the handle for completion by hooking it up to the <code>Completed<\/code> handler. We use the magic <code>auto&amp;&amp;...<\/code> parameter list to say that the lambda accepts any number of arbitrary parameters.<\/p>\n<p>When the asynchronous activity completes, the lambda is invoked, and the lambda throws away the parameters and simply invokes the <code>handle<\/code>, which resumes the coroutine.<\/p>\n<p>When the coroutine resumes, the compiler will call <code>await_<\/code><code>resume<\/code> to find out what the result of the <code>co_await<\/code> is. We call the asynchronous activity&#8217;s <code>Get\u00adResults<\/code> and propagate that as our result using the <code>auto<\/code> return type. (If the asynchronous activity failed with an exception, the <code>Get\u00adResult()<\/code> method will re-raise the exception.)<\/p>\n<p>Since we did no work in the <code>Completed<\/code> handler to get onto any particular thread, the resumption of the coroutine will occur on whatever thread called the <code>Completed<\/code> handler.<\/p>\n<p>Here&#8217;s an example of how you could use it:<\/p>\n<pre>winrt::fire_and_forget DoSomething()\r\n{\r\n  co_await FirstStep();\r\n  co_await resume_in_any_apartment(SecondStep());\r\n  co_await ThirdStep();\r\n}\r\n<\/pre>\n<p>Assuming that all of the steps return <code>IAsync\u00adAction<\/code>, the first and third <code>co_await<\/code>s resume execution in the same apartment, but the second one can resume in any apartment.<\/p>\n<p>Now to add style points:<\/p>\n<pre>template&lt;typename Async,\r\n         <span style=\"color: blue;\">typename = std::enable_if_t&lt;\r\n             std::is_convertible_v&lt;\r\n                 Async,\r\n                 winrt::Windows::Foundation::IAsyncInfo&gt;&gt;<\/span>&gt;\r\n[[nodiscard]] auto resume_in_any_apartment(Async async)\r\n{\r\n  struct awaiter : std::experimental::suspend_always\r\n  {\r\n    <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">awaiter(Async async_) : async(std::move(async_)) { }<\/span><\/span>\r\n\r\n    void await_suspend(\r\n        std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      async.Completed([handle](auto&amp;&amp;amp...) { handle(); });\r\n    }\r\n\r\n    auto await_resume()\r\n    {\r\n        return async.GetResults();\r\n    }\r\n    Async async;\r\n  };\r\n  return awaiter{ <span style=\"color: blue;\">{}, std::move(async)<\/span> };\r\n};\r\n<\/pre>\n<p>We tweak the template parameters so that the overload is eligible only if the <code>Async<\/code> is convertible to <code>IAsync\u00adInfo<\/code>, which is an interface common to all of the Windows Runtime asynchronous activities. That way, if you try to use this with the wrong type, you get a more helpful error message saying that no suitable overload of <code>resume_<\/code><code>in_<\/code><code>any_<\/code><code>apartment<\/code> was found, rather than issuing a weird error message about a missing <code>Completed<\/code> method.<\/p>\n<p>We also remove the constructor of the custom awaiter and instead construct it via aggregate construction. The empty braces initialize the <code>suspend_<\/code><code>always<\/code> base class, and the <code>std::move(async)<\/code> initializes the awaiter&#8217;s <code>async<\/code> member.<\/p>\n<p>Next time, we&#8217;ll look at a feature of custom awaiters that is useful to avoid runaway stack consumption.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Another use for a custom awaiter.<\/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-103207","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Another use for a custom awaiter.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103207","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=103207"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103207\/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=103207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}