{"id":107170,"date":"2022-09-13T07:00:00","date_gmt":"2022-09-13T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107170"},"modified":"2022-09-13T06:36:04","modified_gmt":"2022-09-13T13:36:04","slug":"20220913-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220913-00\/?p=107170","title":{"rendered":"Creating a manual-start C++\/WinRT coroutine from an eager-start one, part 1"},"content":{"rendered":"<p>C++\/WinRT coroutines are eager-start, meaning that they start running as soon as they are created, rather than waiting for you to call a method like <code>Start<\/code> to get them started. We saw last time that <a title=\"Serializing asynchronous operations in C#\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220912-30\/?p=107168\"> we sometimes want to be able to start a coroutine lazily<\/a>. We can port the C# solution to C++\/WinRT:<\/p>\n<pre>template&lt;typename Make&gt;\r\nauto MakeLazy(Make make) -&gt; decltype(make())\r\n{\r\n    auto start = winrt::handle(winrt::check_pointer(\r\n        CreateEvent(nullptr, false, false, nullptr)));\r\n    auto startHandle = start.get();\r\n    auto currentTask = [](auto start, auto make)\r\n        -&gt; decltype(make()) {\r\n        winrt::apartment_context context;\r\n        co_await winrt::resume_on_signal(start.get());\r\n        co_await context;\r\n        co_return co_await make();\r\n    }(std::move(start), std::move(make));\r\n\r\n    \/\/ Resume the coroutine\r\n    SetEvent(startHandle);\r\n    return currentTask;\r\n}\r\n<\/pre>\n<p>We create a kernel event, which is a rather convenient awaitable object built into C++\/WinRT, save its handle, and transfer ownership into the lambda. We also transfer the maker into the lambda so it can make the eager-started task.<\/p>\n<p>After creating and starting the lambda task (which has no captures, because <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190116-00\/?p=100715\"> capturing into a coroutine lambda is a bad idea<\/a>), the lambda task captures its current context (we&#8217;ll see why later) and then waits for the kernel event. This has the effect of a lazy-start coroutine, since it pauses before doing any work.<\/p>\n<p>Back in the main function, after everything is all settled, we set the event handle, which wakes up the <code>resume_<wbr \/>on_<wbr \/>signal<\/code>, and then we return the coroutine that we just started.<\/p>\n<p>After <code>resume_<wbr \/>on_<wbr \/>signal<\/code> resumes, the lambda coroutine awaits the original context in order to resume execution in the same context in which it had started. Whereas <code>co_await<\/code>ing an <code>IAsyncAction<\/code> or <code>IAsyncOperation<\/code> resume in the same COM context in which they started, the <code>resume_<wbr \/>on_<wbr \/>signal<\/code> does not offer the same guarantee. We need to <code>co_await context<\/code> to get back into the original context.<\/p>\n<p>Once resumed, we can ask the maker to produce the eager-started coroutine, which we then await and propagate.<\/p>\n<p>Of course, there&#8217;s not much point here to creating a lazy-start wrapper around an eager-start coroutine, only to start it immediately. But you can imagine splitting the two steps:<\/p>\n<pre>template&lt;typename Make&gt;\r\nauto MakeLazy(Make make) -&gt; <span style=\"color: blue;\">std::pair&lt;HANDLE, decltype(make())&gt;<\/span>\r\n{\r\n    auto start = winrt::handle(winrt::check_pointer(\r\n        CreateEvent(nullptr, false, false, nullptr)));\r\n    auto startHandle = start.get();\r\n    auto currentTask = [](auto start, auto make)\r\n        -&gt; decltype(make()) {\r\n        winrt::apartment_context context;\r\n        co_await winrt::resume_on_signal(start.get());\r\n        co_await context;\r\n        co_return co_await make();\r\n    }(std::move(start), std::move(make));\r\n    <span style=\"color: blue;\">return { startHandle, currentTask }<\/span>;\r\n}\r\n<\/pre>\n<p>This gives you a kernel handle, which you can signal to start the task, and it also gives you a task you can <code>co_await<\/code> to get things started.<\/p>\n<p>This is a fairly straightforward translation of the C# lazy-start wrapper, but it turns out that we can do something more efficient if we are willing to roll up our sleeves and work with the C++ coroutine infrastructure. We&#8217;ll look at that next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Ready, wait for 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-107170","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Ready, wait for it.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107170","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=107170"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107170\/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=107170"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107170"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107170"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}