{"id":105171,"date":"2021-04-30T07:00:00","date_gmt":"2021-04-30T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105171"},"modified":"2021-04-30T06:58:54","modified_gmt":"2021-04-30T13:58:54","slug":"20210430-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210430-00\/?p=105171","title":{"rendered":"C++ coroutines: Waiting synchronously for our coroutine to complete"},"content":{"rendered":"<p>Last time, we <a title=\"C++ coroutines: Adding COM context support to our awaiter\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210429-00\/?p=105165\"> added an extension point that permitted us to respond differently to the completion of the coroutine<\/a>. We&#8217;re going to put that extension point to good use by adding the ability to wait synchronously for the coroutine to complete.<\/p>\n<pre>namespace async_helpers::details\r\n{\r\n    template&lt;typename T&gt;\r\n    struct simple_task_base\r\n    {\r\n        ...\r\n\r\n        <span style=\"color: blue;\">T get() &amp;&amp;\r\n        {\r\n            if (!promise-&gt;client_await_ready()) {\r\n                bool completed = false;\r\n                if (promise-&gt;client_await_suspend(\r\n                    &amp;completed, wake_by_address)) {\r\n                    bool ready = true;\r\n                    while (!completed) {\r\n                        WaitOnAddress(&amp;completed, &amp;ready,\r\n                            sizeof(completed), INFINITE);\r\n                    }\r\n                }\r\n            }\r\n            return std::exchange(promise, {})-&gt;client_await_resume();\r\n        }<\/span>\r\n\r\n        ...\r\n    private:\r\n        ...\r\n\r\n        <span style=\"color: blue;\">static void CALLBACK\r\n            wake_by_address(void* completed)\r\n        {\r\n            *reinterpret_cast&lt;bool*&gt;(completed) = true;\r\n            WakeByAddressSingle(completed);\r\n        }<\/span>\r\n\r\n    };\r\n}\r\n<\/pre>\n<p>To wait synchronously for the coroutine to complete, we first check if the coroutine is already finished. If so, then we&#8217;re done, and we can skip the waiting step.<\/p>\n<p>Otherwise, the coroutine is still running, so we register our suspension with a pointer and a callback function. This time, the pointer is a pointer to a variable that we will set when the coroutine is complete, and the callback is a function that sets that variable. In our case, we use <code>Wait\u00adOn\u00adAddress<\/code> to create a synchronization object out of nothing.<\/p>\n<p>If <code>client_<wbr \/>await_<wbr \/>suspend<\/code> returns <code>false<\/code>, then it means that the coroutine has already completed while we were preparing to suspend, so we should skip the suspend and go straight to the resume step.<\/p>\n<p>Finally, we ask <code>client_<wbr \/>await_<wbr \/>resume<\/code> to obtain the completed value and return it. We use <code>std::exchange<\/code> to cause the <code>promise_ptr<\/code> to be emptied after we get the completed value, thereby consuming it and freeing the coroutine state.<\/p>\n<p>Synchronously waiting for a coroutine is always a risky proposition because the coroutine you&#8217;re waiting for might need to use the thread that you are waiting on. This is particularly true if you perform the wait from a single-threaded COM apartment, because the coroutine will probably need to get back to the original thread to continue its COM work, which it can&#8217;t do because you&#8217;ve blocked the original thread in order to wait for the coroutine.<\/p>\n<p>So don&#8217;t do that.<\/p>\n<p>Next time, we&#8217;ll look at task interconvertibility.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using the extension point for another purpose.<\/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-105171","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Using the extension point for another purpose.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105171","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=105171"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105171\/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=105171"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105171"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105171"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}