{"id":103210,"date":"2019-12-13T07:00:00","date_gmt":"2019-12-13T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103210"},"modified":"2019-12-12T21:41:18","modified_gmt":"2019-12-13T05:41:18","slug":"20191213-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191213-00\/?p=103210","title":{"rendered":"C++ coroutines: Short-circuiting suspension, part 1"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191209-00\/?p=103195\"> At the start of this series<\/a>, I gave the basic idea for how the compiler generates code for <code>co_await<\/code>, but I left out some details for expository simplicity. There are some mysterious steps called &#8220;We&#8217;re not ready to talk about this step yet.&#8221;<\/p>\n<p>Now it&#8217;s time to talk about one of those steps.<\/p>\n<p>It may be the case that when you get around to doing the <code>await_<\/code><code>suspend<\/code>, the thing your custom awaiter is waiting for has already completed. It could be that the operation completed synchronously, or that it was so fast that it finished even before you could schedule the completion.<\/p>\n<p>You don&#8217;t want to invoke the handle directly from your <code>await_<\/code><code>suspend<\/code>, because that would run the coroutine continuation as a subroutine inside the suspension:<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"0\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\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; border-bottom: none;\">\n<div>save state for resumption<\/div>\n<div><code>awaiter.await_suspend(handle);<\/code><\/div>\n<div style=\"padding-left: 1em;\"><code>handle()<\/code><\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px black; border-style: none solid;\">\u00a0<\/td>\n<td style=\"border: solid 1px black;\">\n<div>restore state after resumption<\/div>\n<div><code>result = awaiter.await_resume();<\/code><\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: 1px black; border-style: none solid;\">\u00a0<\/td>\n<td>\n<div>execution continues<\/div>\n<div>coroutine finishes<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black; border-top: none;\">\n<div style=\"padding-left: 1em;\"><code>handle()<\/code> returns<\/div>\n<div>return to caller<\/div>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>This can result in quite a significant accumulation of stack frames if there are a lot of consecutive <code>co_await<\/code>s of already-completed operations.<\/p>\n<p>To avoid this problem, you can change your <code>await_<\/code><code>suspend<\/code> member to return <code>bool<\/code>. Your implementation should check whether the operation has already completed. If so, then do <i>not<\/i> schedule the handle for execution, but instead just return <code>false<\/code>, to indicate that suspension should be abandoned and that execution should resume immediately. Otherwise, schedule the handle for execution as usual, and return <code>true<\/code>.<\/p>\n<p>Adding to our gradually-improving understanding of the compiler code generation of <code>co_await<\/code>:<\/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>(We&#8217;re not ready to talk about this step yet.)<\/div>\n<div>save state for resumption<\/div>\n<div><code>if (awaiter.await_suspend(handle))<\/code> <span style=\"color: red;\">\u21d0<\/span><\/div>\n<div><code>{<\/code><\/div>\n<div style=\"padding-left: 1em;\">return to caller<\/div>\n<\/td>\n<\/tr>\n<tr>\n<td style=\"border: solid 1px black;\">\n<div style=\"padding-left: 1em;\">[Invoking the handle resumes execution here]<\/div>\n<div><code>}<\/code><\/div>\n<div>restore state after resumption<\/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>Let&#8217;s add fancy <code>await_<\/code><code>suspend<\/code> support to our <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 : std::experimental::suspend_always\r\n  {\r\n    <span style=\"color: blue;\">bool<\/span> await_suspend(\r\n        std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      <span style=\"color: blue;\">if (async.Status() != Windows::Foundation::AsyncStatus::Started) {\r\n        return false;\r\n      }<\/span>\r\n      async.Completed([handler](auto&amp;&amp;amp...) { handler(); });\r\n      <span style=\"color: blue;\">return true;<\/span>\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>If at the time of suspension, the asynchronous activity is not in the <code>Started<\/code> state, then that means that it completed (successfully, with an error, or with cancellation). Therefore, there&#8217;s no point waiting for it to complete. We can report it as already-completed and continue execution directly.\u00b9<\/p>\n<p>This type of short-circuit is commonly seen when the <code>await_<\/code><code>suspend<\/code> function tries to schedule the continuation, and the framework says, &#8220;Dude, it&#8217;s already done!&#8221; For example, you might be performing an asynchronous read: If the <code>Read\u00adFile<\/code> function returns <code>TRUE<\/code>, then the operation completed synchronously, and you can go straight to the resumption code.<\/p>\n<p>There&#8217;s one last piece of the compiler code generation that is marked &#8220;We&#8217;re not ready to talk about this step yet.&#8221; We&#8217;re just about ready to talk about that step. Next time.<\/p>\n<p>\u00b9 There is a small race window if the asynchronous activity completes just after we check whether it has completed. Therefore, this change does not eliminate the stack accumulation completely, but it greatly reduces its likelihood.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Avoiding needless stack buildup.<\/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-103210","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Avoiding needless stack buildup.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103210","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=103210"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103210\/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=103210"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103210"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103210"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}