{"id":105054,"date":"2021-04-05T07:00:00","date_gmt":"2021-04-05T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105054"},"modified":"2021-04-06T21:52:51","modified_gmt":"2021-04-07T04:52:51","slug":"20210405-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210405-00\/?p=105054","title":{"rendered":"C++ coroutines: Making the promise itself be the shared state, the outline"},"content":{"rendered":"<p>Last time, we <a title=\"C++ coroutines: Making the promise itself be the shared state, the inspiration\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210402-00\/?p=105047\"> got the idea of putting the result holder state directly inside the coroutine state<\/a>. This time, we&#8217;ll set to work on the implementation.<\/p>\n<p>A restriction we are placing on our <code>simple_task<\/code> is that it can be <code>co_await<\/code>ed only once. This enables the return of a move-only object, and avoid potentially-expensive copy operations. It also discourages some inefficient usage patterns, which we&#8217;ll discuss later.<\/p>\n<p>I&#8217;ll present the code without some of the annoying bits, and then we&#8217;ll spend the next few days filling it in. The code is conceptually simple, but there&#8217;s a lot of paperwork. Placeholders are marked with \u27e6brackets\u27e7.<\/p>\n<pre>namespace async_helpers\r\n{\r\n    template&lt;typename T&gt; struct simple_task;\r\n}\r\nnamespace async_helpers::details\r\n{\r\n    template&lt;typename T&gt; struct simple_promise;\r\n\r\n    \u27e6simple_promise_result_holder definition\u27e7 \r\n   \r\n    template&lt;typename T&gt;\r\n    struct simple_promise_base\r\n    {\r\n        std::atomic&lt;uint32_t&gt; m_refcount{ 2 };\r\n        std::mutex m_mutex;\r\n        std::experimental::coroutine_handle&lt;&gt; m_waiting{ nullptr };\r\n        simple_promise_result_holder&lt;T&gt; m_holder;\r\n\r\n        using Promise = simple_promise&lt;T&gt;;\r\n        auto as_promise() noexcept\r\n        {\r\n            return static_cast&lt;Promise*&gt;(this);\r\n        }\r\n\r\n        \u27e6simple_promise_base reference count methods\u27e7\r\n\r\n        auto get_return_object() noexcept\r\n        {\r\n            return simple_task&lt;T&gt;(as_promise());\r\n        }\r\n\r\n        std::experimental::suspend_never initial_suspend() noexcept\r\n        {\r\n            return {};\r\n        }\r\n\r\n        template&lt;typename...Args&gt;\r\n        void set_value(Args&amp;&amp;... args)\r\n        {\r\n            m_holder.set_value(std::forward&lt;Args&gt;(args)...);\r\n        }\r\n\r\n        void unhandled_exception() noexcept\r\n        {\r\n            m_holder.unhandled_exception();\r\n        }\r\n\r\n        auto final_suspend() noexcept\r\n        {\r\n            \u27e6return an awaiter that decrements the reference count\r\n             and resumes any awaiting coroutine\u27e7\r\n        }\r\n\r\n        \u27e6awaiter support methods\u27e7\r\n\r\n        auto get_awaiter() noexcept\r\n        {\r\n            \u27e6return an awaiter that waits for the coroutine\r\n             to complete\u27e7\r\n        }\r\n    };\r\n\r\n    template&lt;typename T&gt;\r\n    struct simple_promise : simple_promise_base&lt;T&gt;\r\n    {\r\n        \u27e6implement return_value\u27e7\r\n    };\r\n\r\n    template&lt;&gt;\r\n    struct simple_promise&lt;void&gt; : simple_promise_base&lt;void&gt;\r\n    {\r\n        \u27e6implement return_void\u27e7\r\n    };\r\n\r\n    \/\/ promise_ptr&lt;T&gt; is a reference-counted\r\n    \/\/ pointer to a simple_promise&lt;T&gt;\r\n    \u27e6implement promise_ptr\u27e7\r\n}\r\n\r\nnamespace async_helpers\r\n{\r\n    template&lt;typename T&gt;\r\n    struct simple_task\r\n    {\r\n        details::promise_ptr&lt;T&gt; promise;\r\n        simple_task(details::simple_promise&lt;T&gt;*\r\n            initial = nullptr) : promise(initial) {}\r\n\r\n        void swap(simple_task&amp; other)\r\n        {\r\n            std::swap(promise, other.promise);\r\n        }\r\n\r\n        auto operator co_await() const\r\n        {\r\n            return promise-&gt;get_awaiter();\r\n        }\r\n    };\r\n\r\n    template&lt;typename T&gt;\r\n    void swap(simple_task&lt;T&gt;&amp; left, simple_task&lt;T&gt;&amp; right)\r\n    {\r\n        left.swap(right);\r\n    }\r\n}\r\n\r\ntemplate &lt;typename T, typename... Args&gt;\r\nstruct std::experimental::coroutine_traits&lt;\r\n    async_helpers::simple_task&lt;T&gt;, Args...&gt;\r\n{\r\n    using promise_type =\r\n        async_helpers::details::simple_promise&lt;T&gt;;\r\n};\r\n<\/pre>\n<p>I put it all out there at one go just to highlight the overall shape. But let&#8217;s go through it more slowly.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct simple_promise_base\r\n    {\r\n        std::atomic&lt;uint32_t&gt; m_refcount{ 2 };\r\n<\/pre>\n<p>The initial reference count of the promise is two: One reference is held by the coroutine itself because the coroutine keeps its promise alive until it completes. The other reference is held by the <code>simple_task<\/code> that is the return value of the coroutine function.<\/p>\n<pre>        std::mutex m_mutex;\r\n        std::experimental::coroutine_handle&lt;&gt; m_waiting{ nullptr };\r\n        simple_promise_result_holder&lt;T&gt; m_holder;\r\n<\/pre>\n<p>We need a mutex to protect the <code>m_waiting<\/code> variable so it can be updated atomically with respect to state changes. And of course we have the object that holds the result of the coroutine (successful completion result or an exception).<\/p>\n<pre>        using Promise = simple_promise&lt;T&gt;;\r\n        auto as_promise() noexcept\r\n        {\r\n            return static_cast&lt;Promise*&gt;(this);\r\n        }\r\n<\/pre>\n<p>The <code>simple_<wbr \/>promise_<wbr \/>base<\/code> is a CRTP-like type whose derived type is always a <code>simple_<wbr \/>promise&lt;T&gt;<\/code>. We create a type alias <code>Promise<\/code> to refer to that full <code>simple_promise<\/code> type and a helper function to produce a pointer to that type.<\/p>\n<pre>        \u27e6simple_promise_base reference count methods\u27e7\r\n<\/pre>\n<p>Managing the reference counts is a major hassle, so I&#8217;ll defer that discussion as well. Neither the result holder nor the reference count is particularly complicated, but they&#8217;re rather wordy, and there are some subtle parts that deserve closer discussion.<\/p>\n<pre>        auto get_return_object() noexcept\r\n        {\r\n            return simple_task&lt;T&gt;(as_promise());\r\n        }\r\n<\/pre>\n<p>This produces the <code>simple_<wbr \/>task<\/code> that is the formal return value of the coroutine function. The caller is expected to <code>co_await<\/code> this <code>simple_<wbr \/>task<\/code> to get the result of the coroutine function.<\/p>\n<pre>        std::experimental::suspend_never initial_suspend() noexcept\r\n        {\r\n            return {};\r\n        }\r\n<\/pre>\n<p>As I noted, this is a hot-start coroutine, so there is nothing to do at the initial suspension.<\/p>\n<pre>        template&lt;typename...Args&gt;\r\n        void set_value(Args&amp;&amp;... args)\r\n        {\r\n            m_holder.set_value(std::forward&lt;Args&gt;(args)...);\r\n        }\r\n\r\n        void unhandled_exception() noexcept\r\n        {\r\n            m_holder.unhandled_exception();\r\n        }\r\n<\/pre>\n<p>These are the methods which store the coroutine result in the result holder. Don&#8217;t be scared by the variadic template parameter list for <code>set_<wbr \/>value<\/code>. The actual parameter list to <code>set_<wbr \/>value<\/code> will be either empty (for <code>void<\/code>) or a single parameter (for non-<code>void<\/code>). We forward the results into the holder, or if the coroutine function throws an exception, then we capture it as an exception. We&#8217;ll look at these more closely when we study the result holder.<\/p>\n<pre>        auto final_suspend() noexcept\r\n        {\r\n            \u27e6return an awaiter that decrements the reference count\r\n             and resumes any awaiting coroutine\u27e7\r\n        }\r\n<\/pre>\n<p>One of our earlier improvements was to delay resuming any awaiting coroutines until we reach the <code>final_<wbr \/>suspend<\/code>. The additional wrinkle here is that when the coroutine reaches its final suspension point, we decrement the reference count on the promise, which might or might not trigger destruction of the coroutine state. We&#8217;ll discuss this some more later.<\/p>\n<pre>        \u27e6awaiter support methods\u27e7\r\n\r\n        auto get_awaiter() noexcept\r\n        {\r\n            \u27e6return an awaiter that waits for the coroutine\r\n             to complete\u27e7\r\n        }\r\n    };\r\n<\/pre>\n<p>The <code>get_awaiter<\/code> method produces an awaiter that waits for the coroutine to complete and returns the result (either in the form of a value or an exception). We&#8217;ve basically seen this before in our <code>result_<wbr \/>holder<\/code>, but the wrinkles are slightly different due to our ability to process move-only types. We&#8217;ll see more about this later.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct simple_promise : simple_promise_base&lt;T&gt;\r\n    {\r\n        \u27e6implement return_value\u27e7\r\n    };\r\n\r\n    template&lt;&gt;\r\n    struct simple_promise&lt;void&gt; : simple_promise_base&lt;void&gt;\r\n    {\r\n        \u27e6implement return_void\u27e7\r\n    };\r\n<\/pre>\n<p>As I noted earlier, it is not legal for a promise to have both <code>return_value<\/code> and <code>return_void<\/code>, so we have to split them into separate classes. We&#8217;ll look at the implementation later, because there are some annoyances here.<\/p>\n<pre>    \/\/ promise_ptr&lt;T&gt; is a reference-counted\r\n    \/\/ pointer to a simple_promise&lt;T&gt;\r\n    \u27e6implement promise_ptr\u27e7\r\n}\r\n<\/pre>\n<p>The <code>promise_ptr<\/code> is a reference-counted pointer to our <code>simple_promise<\/code>. This class is basically all-annoying with nothing of interest inside it. I&#8217;ll defer its implementation to later.<\/p>\n<pre>namespace async_helpers\r\n{\r\n    template&lt;typename T&gt;\r\n    struct simple_task\r\n    {\r\n        simple_task(details::simple_promise&lt;T&gt;*\r\n            initial = nullptr) : promise(initial) {}\r\n\r\n        void swap(simple_task&amp; other)\r\n        {\r\n            std::swap(promise, other.promise);\r\n        }\r\n\r\n        auto operator co_await() const\r\n        {\r\n            return promise-&gt;get_awaiter();\r\n        }\r\n    private:\r\n        details::promise_ptr&lt;T&gt; promise;\r\n    };\r\n\r\n    template&lt;typename T&gt;\r\n    void swap(simple_task&lt;T&gt;&amp; left, simple_task&lt;T&gt;&amp; right)\r\n    {\r\n        left.swap(right);\r\n    }\r\n}\r\n<\/pre>\n<p>The <code>simple_task<\/code> itself is very simple. It wraps a reference-counted pointer to the <code>simple_promise<\/code> and forwards its <code>co_await<\/code> operator to the promise&#8217;s awaiter. When the <code>simple_task<\/code> destructs, the reference in the <code>promise_ptr<\/code> is released, and that takes us one step closer to the end of the coroutine state. (Of course, if you copy the <code>simple_task<\/code>, then the reference count goes up, and you end up extending the lifetime further.)<\/p>\n<p>Half of the code is just there to support ADL swap!<\/p>\n<pre>template &lt;typename T, typename... Args&gt;\r\nstruct std::experimental::coroutine_traits&lt;\r\n    async_helpers::simple_task&lt;T&gt;, Args...&gt;\r\n{\r\n    using promise_type =\r\n        async_helpers::details::simple_promise&lt;T&gt;;\r\n};\r\n<\/pre>\n<p>Finally, we tell the coroutine infrastructure that if somebody writes<\/p>\n<pre>async_helpers::simple_task&lt;T&gt; MyCoroutine()\r\n{\r\n    ...\r\n    co_return ...;\r\n}\r\n<\/pre>\n<p>then it should use the <code>simple_promise<\/code> to assist with the implementation of the coroutine.<\/p>\n<p>Okay, so that&#8217;s it! There&#8217;s a lot of paperwork, but the basic idea is that the promise is where all the action is. The coroutine and the task each have a reference to the promise, and that&#8217;s how the coroutine and the task communicate with each other.<\/p>\n<p>Oh wait, I have a lot of code to fill in. We&#8217;ll start that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Now we do it.<\/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-105054","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Now we do it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105054","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=105054"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105054\/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=105054"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105054"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105054"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}