{"id":105128,"date":"2021-04-20T08:01:28","date_gmt":"2021-04-20T15:01:28","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105128"},"modified":"2021-04-20T08:01:28","modified_gmt":"2021-04-20T15:01:28","slug":"20210420-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210420-28\/?p=105128\/","title":{"rendered":"C++ coroutines: Getting rid of our atomic variant discriminator"},"content":{"rendered":"<p>We continue <a title=\"C++\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210416-00\/?p=105115\"> the refinement of our coroutine implementation<\/a> by removing the atomic variable used as the discriminant of our result holder variant.<\/p>\n<p>The discriminant needed to be atomic because we used it in <code>await_<wbr \/>ready<\/code> to peek at whether the coroutine had completed. But we&#8217;ve switched over to using the atomic <code>m_waiting<\/code> member to track the coroutine state, which means that the use of the discriminant is now protected by the memory ordering requirements of <code>m_waiting<\/code>. The discriminant itself can now be a regular variable.<\/p>\n<pre>    template&lt;typename T&gt;\r\n    struct simple_promise_holder\r\n    {\r\n        ...\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">std::atomic&lt;result_status&gt;<\/span><\/span>\r\n        <span style=\"color: blue;\">result_status<\/span> status\r\n            { result_status::empty };\r\n\r\n        template&lt;typename...Args&gt;\r\n        void set_value(Args&amp;&amp;... args)\r\n        {\r\n            new (std::addressof(result.wrap))\r\n                wrapper&lt;T&gt;{ std::forward&lt;Args&gt;(args)... };\r\n            <span style=\"color: blue;\">status = result_status::value<\/span>;\r\n        }\r\n\r\n        void unhandled_exception() noexcept\r\n        {\r\n            new (std::addressof(result.error))\r\n                std::exception_ptr(std::current_exception());\r\n            <span style=\"color: blue;\">status = result_status::error<\/span>;\r\n        }\r\n\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">bool is_empty() const noexcept<\/span><\/span>\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">{<\/span><\/span>\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">    return status.load(std::memory_order_relaxed) ==<\/span><\/span>\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">        result_status::empty;<\/span><\/span>\r\n        <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">}<\/span><\/span>\r\n\r\n        T get_value()\r\n        {\r\n            switch (<span style=\"color: blue;\">status<\/span>) {\r\n            case result_status::value:\r\n                return result.wrap.get_value();\r\n            case result_status::error:\r\n                std::rethrow_exception(\r\n                    std::exchange(result.error, {}));\r\n            }\r\n            assert(false);\r\n            std::terminate();\r\n        }\r\n\r\n        ~simple_promise_result_holder()\r\n        {\r\n            switch (<span style=\"color: blue;\">status<\/span>) {\r\n            case result_status::value:\r\n                result.wrap.~wrapper();\r\n                break;\r\n            case result_status::error:\r\n                if (result.error)\r\n                    std::rethrow_exception(result.error);\r\n                result.error.~exception_ptr();\r\n            }\r\n        }\r\n    };\r\n<\/pre>\n<p>What used to be <code>status.store<\/code> is now just an assignment, and what used to be <code>status.load<\/code> now just a read.<\/p>\n<p>We can also get rid of the <code>is_empty<\/code> method, since it was used only by our previous version of <code>client_<wbr \/>await_<wbr \/>ready<\/code>, which we abandoned when we switched to using <code>m_waiting<\/code>.<\/p>\n<p>Next time, we&#8217;ll add support for cold-start coroutines.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Don&#8217;t look unless you know it&#8217;s safe.<\/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-105128","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Don&#8217;t look unless you know it&#8217;s safe.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105128","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=105128"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105128\/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=105128"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105128"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105128"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}