{"id":104987,"date":"2021-03-23T07:00:00","date_gmt":"2021-03-23T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104987"},"modified":"2021-03-23T09:19:16","modified_gmt":"2021-03-23T16:19:16","slug":"20210323-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210323-00\/?p=104987","title":{"rendered":"Creating a task completion source for a C++ coroutine: Producing a result"},"content":{"rendered":"<p>We&#8217;ve been looking at <a title=\"Creating other types of synchronization objects that can be used with co_await, part 3: Parallel resumption\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210311-00\/?p=104949\"> creating different types of awaitable synchronization objects<\/a>. This time, we&#8217;ll create something analogous to what C# calls a <code>Task\u00adCompletion\u00adSource<\/code> and what PPL calls a <code>task_<wbr \/>completion_<wbr \/>event<\/code>. For lack of a better name, I&#8217;m going to call it a <code>result_<wbr \/>holder<\/code>.<\/p>\n<p>A <code>result_<wbr \/>holder<\/code> is an object that you can put a result into, and you can <code>co_await<\/code> it to wait for the result to appear. Once a result has been set, it can be retrieved multiple times. You can use this sort of object for one-time initialization, or if you want to cache the results of earlier calculations.<\/p>\n<p>First, we need to teach our library about coroutines that return values. Up until now, the result of a <code>co_await<\/code> had always been <code>void<\/code>.<\/p>\n<pre>    template&lt;typename State&gt;\r\n    class awaitable_state\r\n    {\r\n        ...\r\n\r\n        <span style=\"color: blue;\">void get_result() const noexcept { }\r\n\r\n        auto<\/span> await_resume(\r\n            impl::node&lt;extra_await_data&gt;&amp; node) noexcept\r\n        {\r\n            node.handle = nullptr;\r\n            <span style=\"color: blue;\">return parent().get_result();<\/span>\r\n        }\r\n        ...\r\n    };\r\n\r\n    template&lt;typename State&gt;\r\n    class awaitable_sync_object\r\n    {\r\n        ...\r\n\r\n        struct awaiter\r\n        {\r\n            ...\r\n\r\n            <span style=\"color: blue;\">auto<\/span> await_resume()\r\n            { <span style=\"color: blue;\">return<\/span> s.await_resume(node); }\r\n\r\n            ...\r\n        }\r\n    };\r\n<\/pre>\n<p>We allow the CRTP client to implement a method <code>get_<wbr \/>result<\/code>, and whatever that method returns is the result of the <code>co_await<\/code>. By default, it&#8217;s just <code>void<\/code>, but we&#8217;re going to override it in our <code>result_<wbr \/>holder<\/code>.<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct result_holder_state :\r\n    async_helpers::awaitable_state&lt;result_holder_state&lt;T&gt;&gt;\r\n{\r\n    std::atomic&lt;bool&gt; ready{ false };\r\n\r\n    union optional\r\n    {\r\n        optional() {}\r\n        ~optional() {}\r\n\r\n        T value;\r\n    } result;\r\n\r\n    result_holder_state() {}\r\n    result_holder_state(result_holder_state const&amp;) = delete;\r\n    void operator=(result_holder_state const&amp;) = delete;\r\n\r\n    ~result_holder_state()\r\n    {\r\n        if (ready.load(std::memory_order_relaxed)) {\r\n            result.value.~T();\r\n        }\r\n    }\r\n<\/pre>\n<p>We build our own equivalent of <code>std::optional&lt;T&gt;<\/code> that supports querying atomically whether a value has been set. The atomic boolean <code>ready<\/code> becomes <code>true<\/code> when a value is set, and the union <code>result<\/code> holds the value if so. We use a union because unions do not construct or destruct their members by default. But it means that we must remember to do the construction and destruction ourselves.<\/p>\n<p>This is not a general-purpose atomic <code>optional<\/code> because it supports only one-way transitions: You can go from unset to set, but once set, it&#8217;s stuck forever. This limitation allows the discriminant (<code>ready<\/code>) to be atomic.<\/p>\n<pre>    using typename result_holder_state::extra_await_data;\r\n    using typename result_holder_state::node_list;\r\n<\/pre>\n<p>Since our state type is now a template type, we have to tell the compiler which identifiers are dependent names. We may as well just import them to save ourselves some typing.<\/p>\n<pre>    bool fast_claim(extra_await_data const&amp;) noexcept\r\n    {\r\n        return ready.load(std::memory_order_acquire);\r\n    }\r\n\r\n    bool claim(extra_await_data const&amp;) noexcept\r\n    {\r\n        return ready.load(std::memory_order_relaxed);\r\n    }\r\n<\/pre>\n<p>If someone tries to <code>co_await<\/code>, we let the await complete immediately if the value is already ready.<\/p>\n<pre>    void set_result(node_list&amp; list, T v)\r\n    {\r\n        if (!ready.load(std::memory_order_relaxed)) {\r\n            new (std::addressof(result.value))\r\n                T{ std::move&lt;T&gt;(v) };\r\n            ready.store(true, std::memory_order_release);\r\n            this-&gt;resume_all(list);\r\n        }\r\n    }\r\n<\/pre>\n<p>To set the result, we first check that we don&#8217;t have a result. If so, then we do nothing. You can set the result only once. Otherwise, we would have a race condition if one coroutine fetches the value while another is changing it.<\/p>\n<p>If this is the first time anyone is setting the result, then we move the value into our private storage, using the placement new constructor. We provide the storage address via <code>std::addressof<\/code> to protect against the possibility that <code>T<\/code> has an overloaded <code>operator&amp;<\/code>.<\/p>\n<p>Only after the value is definitely set into our private storage do we mark the value as <code>ready<\/code>, and we do it with release semantics so that the effects of the constructor are fully visible before telling everybody that it&#8217;s ready to be read.<\/p>\n<p>It&#8217;s also important to be aware that the constructor of <code>T<\/code> may throw an exception. In that case, the storage is destructed back to its uninitialized state, and the exception escapes. Another reason it&#8217;s important not to set <code>ready<\/code> or to add coroutines to the <code>list<\/code> before the value is definitely constructed.<\/p>\n<pre>    T get_result()\r\n    {\r\n        return result.value;\r\n    }\r\n};\r\n<\/pre>\n<p>And here&#8217;s where we override <code>get_<wbr \/>result<\/code> so that the result of a <code>co_await<\/code> is the captured value.<\/p>\n<p>We technically need an acquire fence here to ensure that all the changes to <code>value<\/code> made by the <code>set_<wbr \/>result<\/code> are visible to the current. We get away without one because we put an acquire fence in <code>await_<wbr \/>ready<\/code>!<\/p>\n<pre>template&lt;typename T&gt;\r\nstruct result_holder\r\n    : async_helpers::awaitable_sync_object&lt;\r\n        result_holder_state&lt;T&gt;&gt;\r\n{\r\n    using typename result_holder::state;\r\n\r\n    void set_result(T result) const noexcept\r\n    {\r\n        this-&gt;action_impl(&amp;state::set_result,\r\n            std::move(result));\r\n    }\r\n};\r\n<\/pre>\n<p>The object itself is not particularly exciting. Setting the result on the main object moves the value into the state.<\/p>\n<p>Now you have an object that you can put results into, and <code>co_await<\/code>ing it will wait until results appear.<\/p>\n<pre>result_holder&lt;int&gt; universe;\r\n\r\n\/\/ coroutine 1:\r\nauto answer = co_await universe;\r\n\r\n\/\/ coroutine 2:\r\nuniverse.set_result(42);\r\n<\/pre>\n<p>But this code is still broken.<\/p>\n<p>We&#8217;ll look more closely next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s call it a result holder.<\/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-104987","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Let&#8217;s call it a result holder.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104987","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=104987"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104987\/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=104987"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104987"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104987"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}