{"id":103217,"date":"2019-12-16T07:00:00","date_gmt":"2019-12-16T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103217"},"modified":"2019-12-18T09:21:28","modified_gmt":"2019-12-18T17:21:28","slug":"20191216-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191216-00\/?p=103217","title":{"rendered":"C++ coroutines: Short-circuiting suspension, part 2"},"content":{"rendered":"<p>There&#8217;s one last section of the outline of compiler code generation for <code>co_await<\/code> that is marked &#8220;We&#8217;re not ready to talk about this step yet.&#8221; Let&#8217;s talk about that step.<\/p>\n<p>Before suspending the coroutine, the compiler asks the awaiter&#8217;s <code>await_ready<\/code> method. This method returns <code>true<\/code> if the operation is already complete, or <code>false<\/code> if the coroutine should suspend.<\/p>\n<p>If the operation is already complete, then the compiler can avoid having to save the coroutine&#8217;s state, only to load it back up again immediately.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<td>&nbsp;<\/td>\n<td>\n<div>calculate <code>x<\/code><\/div>\n<div>obtain <code>awaiter<\/code><\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\" rowspan=\"2\" valign=\"middle\"><code>co_await<\/code><\/td>\n<td style=\"border: solid 1px black;\">\n<div><code>if (!awaiter.await_ready())<\/code> <span style=\"color: red;\">\u21d0<\/span><\/div>\n<div><code>{<\/code><\/div>\n<div style=\"padding-left: 1em;\">save state for resumption<\/div>\n<div style=\"padding-left: 1em;\"><code>if (awaiter.await_suspend(handle))<\/code><\/div>\n<div style=\"padding-left: 1em;\"><code>{<\/code><\/div>\n<div style=\"padding-left: 2em;\">return to caller<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\">\n<div style=\"padding-left: 2em;\">[Invoking the handle resumes execution here]<\/div>\n<div style=\"padding-left: 1em;\"><code>}<\/code><\/div>\n<div style=\"padding-left: 1em;\">restore state after resumption<\/div>\n<div><code>}<\/code> <span style=\"color: red;\">\u21d0<\/span><\/div>\n<div><code>result = awaiter.await_resume();<\/code><\/div>\n<\/td>\n<\/tr>\n<tr>\n<td>&nbsp;<\/td>\n<td>execution continues<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>In the case where <code>await_ready<\/code> says, &#8220;Yes, I&#8217;m ready!&#8221;, the compiler skips over the code that saves the coroutine state, creates a continuation handle, suspends the coroutine, and asks the <code>await_<\/code><code>suspend<\/code> to arrange for the coroutine&#8217;s continuation; and then when the continuation occurs, restoring the coroutine state. Instead, it can go straight to the &#8220;So what was the result?&#8221; This avoids a bunch of register spilling and reloading.<\/p>\n<p>The C++ language comes with a predefined awaiter known as <code>suspend_<\/code><code>never<\/code>. Its <code>await_<\/code><code>ready<\/code> always returns <code>true<\/code>, which means that it never actually suspends. It always goes straight to the continuation.\u00b9<\/p>\n<p>We can take advantage of the <code>await_<\/code><code>ready<\/code> method <code>resume_<\/code><code>in_<\/code><code>any_<\/code><code>apartment<\/code> function:<\/p>\n<pre>template&lt;typename Async,\r\n         typename = std::enable_if_t&lt;\r\n             std::is_convertible_v&lt;\r\n                 Async,\r\n                 winrt::Windows::Foundation::IAsyncInfo&gt;&gt;&gt;\r\n[[nodiscard]] auto resume_in_any_apartment(Async async)\r\n{\r\n  struct awaiter\r\n  {\r\n    <span style=\"color: blue;\">bool await_ready()\r\n    {\r\n      return async.Status() ==\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0  Windows::Foundation::AsyncStatus::Completed;\r\n    }<\/span>\r\n\r\n    void await_suspend(\r\n        std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      async.Completed([handler](auto&amp;&amp;...) { handler(); });\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>Perhaps a clearer example of this pattern is an awaitable which detects that its work is unnecessary, such as this one which switches to the dispatcher&#8217;s thread:<\/p>\n<pre>auto ensure_dispatcher_thread(CoreDispatcher dispatcher)\r\n{\r\n  struct awaiter : std::experimental::suspend_always\r\n  {\r\n    CoreDispatcher dispatcher;\r\n\r\n    bool await_ready() { return dispatcher.HasThreadAccess(); }\r\n\r\n    void await_suspend(\r\n        std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      dispatcher.RunAsync(CoreDispatcherPriority::Normal,\r\n                           [handle]{ handle(); });\r\n    }\r\n  };\r\n  return awaiter{ {}, std::move(dispatcher) };\r\n}\r\n<\/pre>\n<p>This awaitable resumes execution on the dispatcher&#8217;s thread. In the <code>await_<\/code><code>ready<\/code>, we check if we are already on the dispatcher&#8217;s thread. If so, then we report that the <code>co_await<\/code> is complete even before it started, and execution will continue without ever suspending. Otherwise, the coroutine suspends, and we schedule its resumption on the dispatcher&#8217;s thread.<\/p>\n<p>\u00b9 An awaiter that never suspends sounds really strange. After all, why bother even being a coroutine! But it&#8217;s handy for cases in which you have to provide an awaiter even though nothing is being awaited. We&#8217;ll see examples of this when we study the promise object at some unspecified point in the future.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avoiding suspension entirely.<\/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-103217","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Avoiding suspension entirely.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103217","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=103217"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103217\/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=103217"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103217"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103217"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}