{"id":105028,"date":"2021-03-31T07:00:00","date_gmt":"2021-03-31T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105028"},"modified":"2021-04-01T08:17:01","modified_gmt":"2021-04-01T15:17:01","slug":"20210331-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210331-00\/?p=105028\/","title":{"rendered":"C++ coroutines: The initial and final suspend, and improving our return_value method"},"content":{"rendered":"<p>Last time, we <a title=\"C++ coroutines: Basic implementation of a promise type\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210330-00\/?p=105019\"> had a basic implementation of a promise type<\/a> but left with the question of what the <code>initial_<wbr \/>suspend<\/code> and <code>final_<wbr \/>suspend<\/code> are for.<\/p>\n<p>When the compiler encounters a coroutine function, it performs multiple rewrite passes. One of the early passes produces the following:<\/p>\n<pre>return_type MyCoroutine(args...)\r\n{\r\n    <i>create coroutine state<\/i>\r\n    <i>copy parameters to coroutine frame<\/i>\r\n    promise_type p;\r\n    auto return_object = p.get_return_object();\r\n\r\n    try {\r\n        co_await p.initial_suspend(); \/\/ \u00b9\r\n        <i>coroutine function body\u00b2<\/i>\r\n    } catch (...) {\r\n        p.unhandled_exception();\r\n    }\r\n    co_await p.final_suspend();\r\n    <i>destruct promise p<\/i>\r\n    <i>destruct parameters in coroutine frame<\/i>\r\n    <i>destroy coroutine state<\/i>\r\n}\r\n<\/pre>\n<p>This rewrite is where the <code>initial_<wbr \/>suspend<\/code> and <code>final_<wbr \/>suspend<\/code> enter the picture.<\/p>\n<p>After constructing the promise <code>p<\/code>, it calls <code>p.get_<wbr \/>return_<wbr \/>object()<\/code> to obtain the object that is passed back to the caller. Next, the coroutine awaits whatever <code>initial_<wbr \/>suspend()<\/code> returns.<\/p>\n<p>There are two common choices for <code>initial_<wbr \/>suspend()<\/code>: If you return a <code>suspend_<wbr \/>never<\/code> or some other awaiter that doesn&#8217;t suspend the coroutine, then the coroutine keeps running until the first suspending <code>co_await<\/code>. This is the model for &#8220;hot-start&#8221; coroutines which execute synchronously during their construction and don&#8217;t return an object until the first suspension inside the function body.<\/p>\n<p>On the other hand, if you return an awaiter which results in the coroutine suspending, then the caller gets its return object right away. Nothing in the coroutine function body has executed yet. When the caller eventually performs a <code>co_await<\/code> on the return object, the return object resumes the coroutine.\u00b3 This is the model for &#8220;cold-start&#8221; coroutines which don&#8217;t begin executing until they are awaited.<\/p>\n<p>Conversely, there is a <code>final_suspend<\/code> at the end of the coroutine after the coroutine function body has finished. This gives you a chance to do any extra cleanup, as well as perform some other magic we&#8217;ll look at in a future installment.<\/p>\n<p>One way to improve in our implementation of the <code>coroutine_<wbr \/>traits&lt;<wbr \/>result_holder&gt;<\/code> promise type is our handling of <code>return_value<\/code>. First, there&#8217;s a simple improvement of accepting the value by reference rather than value, so we can forward it into the <code>result_<wbr \/>holder<\/code>, thereby avoiding some extra move operations.<\/p>\n<pre>            <span style=\"color: blue;\">void return_value(T&amp;&amp; v) const\r\n            {\r\n                holder.set_result(std::move(v));\r\n            }<\/span>\r\n\r\n            void return_value(T const&amp; v) const\r\n            {\r\n                holder.set_result(v);\r\n            }\r\n<\/pre>\n<p>But there&#8217;s a bigger problem to fix: We release the waiting coroutines too soon.\u2074<\/p>\n<p>Consider a coroutine function that goes like this:<\/p>\n<pre>result_holder&lt;int&gt; SomethingAsync()\r\n{\r\n    auto guard = std::lock_guard{ mutex };\r\n    co_return 42;\r\n}\r\n<\/pre>\n<p>When this function reaches the <code>co_return<\/code>, the compiler generates a call to <code>return_<wbr \/>value<\/code>, and our implementation of <code>return_<wbr \/>value<\/code> sets the result into the holder, which immediately releases any waiting coroutines.<\/p>\n<p>But those waiting coroutines might want to acquire the same mutex that the <code>Something\u00adAsync<\/code> function still owns. You end up in a hurry-up-and-wait situation, where we wake up a coroutine, only to have it block immediately.<\/p>\n<p>The scenario is even more dire if we resume the waiting coroutines synchronously, because the waiting coroutine may want to acquire the mutex, but it can&#8217;t because <code>Something\u00adAsync<\/code> still owns the mutex, and <code>Something\u00adAsync<\/code> won&#8217;t resume execution until after <code>return_<wbr \/>value<\/code> returns, which can&#8217;t happen because it&#8217;s waiting for the resumed coroutine to reach its next suspension point.<\/p>\n<p>More generally, local variables are still alive at the point of the <code>co_return<\/code>, so any resources held by those local variables are still active at the point of the <code>return_<wbr \/>value<\/code>.<\/p>\n<p>The solution is to break the <code>return_<wbr \/>value<\/code> into two steps. The first step executes immediately: Saving the value or exception into the <code>holder<\/code>. But we don&#8217;t wake up the waiting coroutines yet. Leave them suspended for a little while longer.<\/p>\n<p>After the return value or exception is captured, the local variables in the coroutine function body are destructed when we leave the scope of the <code>try<\/code> block that wraps the coroutine function body. Outside the <code>try<\/code> block, we perform a <code>final_<wbr \/>suspend<\/code>, and that&#8217;s where we can take the second step of waking the waiting coroutines.<\/p>\n<p>To implement this, we need to add a few new methods to our <code>result_<wbr \/>holder<\/code>:<\/p>\n<pre>struct result_holder_state\r\n{\r\n    ...\r\n\r\n    void stage_result(T v)\r\n    {\r\n        if (kind.load(std::memory_order_relaxed)\r\n            == result_kind::unset) {\r\n            new (std::addressof(result.wrap))\r\n                wrapper{ std::forward&lt;T&gt;(v) };\r\n            kind.store(result_kind::value,\r\n                std::memory_order_release);\r\n        }\r\n    }\r\n\r\n    void stage_exception(std::exception_ptr ex) noexcept\r\n    {\r\n        if (kind.load(std::memory_order_relaxed)\r\n            == result_kind::unset) {\r\n            new (std::addressof(result.ex))\r\n                std::exception_ptr{ std::move(ex) };\r\n            kind.store(result_kind::exception,\r\n                std::memory_order_release);\r\n        }\r\n    }\r\n\r\n    void complete(node_list&amp; list) noexcept\r\n    {\r\n        this-&gt;resume_all(list);\r\n    }\r\n};\r\n<\/pre>\n<p>We break the <code>set_<wbr \/>result<\/code> into two parts: <code>stage_<wbr \/>result<\/code> puts the result in the state, but doesn&#8217;t resume anybody yet. The resumption of waiting coroutines happens when we call <code>complete<\/code>.<\/p>\n<p>We hook up these methods to the main <code>result_<wbr \/>holder<\/code> class:<\/p>\n<pre>struct result_holder\r\n{\r\n    ...\r\n\r\n    void stage_result(T result) const\r\n    {\r\n        this-&gt;get_state().stage_result(std::move(result));\r\n    }\r\n\r\n    void stage_exception(std::exception_ptr ex) const noexcept\r\n    {\r\n        this-&gt;get_state().stage_exception(std::move(ex));\r\n    }\r\n\r\n    void complete() const noexcept\r\n    {\r\n        this-&gt;action_impl(&amp;state::complete);\r\n    }\r\n};\r\n<\/pre>\n<p>And we revise our promise to use these new methods:<\/p>\n<pre>            void return_value(T&amp;&amp; v) const\r\n            {\r\n                holder.stage_result(std::move(v));\r\n            }\r\n\r\n            void return_value(T const&amp; v) const\r\n            {\r\n                holder.stage_result(v);\r\n            }\r\n\r\n            suspend_never final_suspend() const noexcept\r\n            {\r\n                holder.complete();\r\n                return{};\r\n            }\r\n<\/pre>\n<p>Okay, so we&#8217;ve fixed the problem of resuming the waiting coroutines too soon. There&#8217;s another improvement we can make, but there&#8217;s another topic I want to cover first, which I&#8217;ll do next time.<\/p>\n<p>\u00b9 The actual transformation is more complicated than described here, but the simplified version will suffice for now. One discrepancy that is worth noting here is that an exception that occurs when creating the initial suspend object is <i>not<\/i> caught by the <code>try<\/code> statement; instead, it propagates synchronously out of the coroutine. However, an exception that occurs <i>during<\/i> the <code>co_await<\/code> is indeed caught by the <code>try<\/code> statement and ends up captured into the promise.<\/p>\n<p>\u00b2 If there is a function try around the entire function, the <code>try<\/code> is considered to be part of the function body. See <b>[dcl.fct.def.general]<\/b> for the formal definition of &#8220;function body&#8221;. This transformation of function try is performed even before the transformation described above.<\/p>\n<p>\u00b3 I&#8217;m assuming of course that you provided the return object a way to resume the coroutine when it is awaited. We&#8217;ll explore possible ways of doing this in future installments.<\/p>\n<p>\u2074 My thanks to Gor Nishanov for pointing out this issue to me.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Why would you stop before you start?<\/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-105028","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Why would you stop before you start?<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105028","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=105028"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105028\/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=105028"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105028"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105028"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}