{"id":102350,"date":"2019-03-21T07:00:00","date_gmt":"2019-03-21T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=102350"},"modified":"2019-06-06T17:36:50","modified_gmt":"2019-06-07T00:36:50","slug":"20190321-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190321-00\/?p=102350","title":{"rendered":"Expressing fire-and-forget coroutines more explicitly, -or- How to turn any coroutine into fire-and-forget"},"content":{"rendered":"<p><a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190320-00\/?p=102345\">Last time<\/a>, we looked at how to mark a coroutine as fire-and-forget, meaning that the caller does not get any information about when the coroutine completes. This is fine as far as it goes, but it may not be what you want. <\/p>\n<p>Fire-and-forget-ness is frequently a property of the call site, not the function itself. A particular coroutine could be called with a continuation in one case, but as fire-and-forget in other cases. There should be a way to capture the desired behavior at the call site because it&#8217;s the caller&#8217;s choice whether they want to wait for the result or to proceed without it. <\/p>\n<pre>\r\nusing winrt::Windows::Foundation;\r\n\r\nIAsyncAction DoSomethingAsync()\r\n{\r\n  co_await blah();\r\n  co_await blah();\r\n  co_await blah();\r\n}\r\n\r\n\/\/ This caller cares about when the coroutine completes.\r\nIAsyncAction DoSomethingAndThenSomethingElseAsync()\r\n{\r\n  co_await DoSomethingAsync();\r\n  DoSomethingElse();\r\n}\r\n\r\n\/\/ This caller doesn't care\r\nvoid StartDoingSomethingAndSomethingElse()\r\n{\r\n  \/\/ Don't co_await this; just let it go.\r\n  DoSomethingAsync();\r\n\r\n  \/\/ This runs while the DoSomethingAsync is still in progress.\r\n  DoSomethingElse();\r\n}\r\n<\/pre>\n<p>Calling <code>Do&shy;Something&shy;Async<\/code> and throwing away the <code>IAsync&shy;Action<\/code> is dangerous: If an unhandled exception occurs in the task, there is nobody around to observe it, and you&#8217;re back to where you were with the <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190320-00\/?p=102345\">overly forgetful <code>winrt::fire_<\/code><code>and_<\/code><code>forget<\/code><\/a>. <\/p>\n<p>On the other hand, we don&#8217;t want to write two versions of <code>Do&shy;Something&shy;Async<\/code>, one which returns an <code>IAsync&shy;Action<\/code> and another which returns a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>. We should be able to convert any <code>IAsync&shy;Action<\/code> into a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>. <\/p>\n<pre>\r\ntemplate&lt;typename T&gt;\r\nwinrt::fire_and_forget no_await(T t)\r\n{\r\n co_await t;\r\n}\r\n<\/pre>\n<p>Now you can declare at the call site that you don&#8217;t care about the completion (aside from ensuring that it doesn&#8217;t trigger any unhandled exceptions). <\/p>\n<pre>\r\nIAsyncAction DoSomethingAndThenSomethingElseAsync()\r\n{\r\n  co_await DoSomethingAsync();\r\n\r\n  \/\/ This doesn't run until the DoSomethingAsync completes.\r\n  DoSomethingElse();\r\n}\r\n\r\n\/\/ This caller doesn't care\r\nvoid StartDoingSomethingAndSomethingElse()\r\n{\r\n  \/\/ This starts and we don't want for it to complete.\r\n  no_await(DoSomethingAsync());\r\n\r\n  \/\/ This runs while the DoSomethingAsync is still in progress.\r\n  DoSomethingElse();\r\n}\r\n<\/pre>\n<p>This helper is useful when employed <a HREF=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20190118-00\/?p=100735\">in conjunction with <code>invoke_<\/code><code>async_<\/code><code>lambda<\/code><\/a>. <\/p>\n<pre>\r\nvoid OnClick()\r\n{\r\n no_await(invoke_async_lambda([=]() -&gt; IAsyncAction\r\n {\r\n   ... do stuff, including co_await ...\r\n }));\r\n}\r\n<\/pre>\n<p>The combination is useful enough that you might want a helper that does both. <\/p>\n<pre>\r\ntemplate&lt;typename T&gt;\r\nwinrt::fire_and_forget no_await_lambda(T t)\r\n{\r\n co_await t();\r\n}\r\n<\/pre>\n<p>Recall that the subtlety of <code>invoke_<\/code><code>async_<\/code><code>lambda<\/code> is that it copies the lambda into its frame, so that its lifetime will extend until the coroutine completes. But <code>no_<\/code><code>await<\/code> already copies the lambda into its frame, so the make work of <code>invoke_<\/code><code>async_<\/code><code>lambda<\/code> is already taken care of! All that&#8217;s left is to <code>co_<\/code><code>await<\/code> it into a <code>winrt::<\/code><code>fire_<\/code><code>and_<\/code><code>forget<\/code>. <\/p>\n<p>Next time, we&#8217;ll try to unify <code>no_<\/code><code>await<\/code> and <code>no_<\/code><code>await_<\/code><code>lambda<\/code>, mostly because I think the name <code>no_<\/code><code>await<\/code> is really cute and I don&#8217;t want to give it up. <\/p>\n","protected":false},"excerpt":{"rendered":"<p>Putting it at the call site instead of in the function itself.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-102350","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Putting it at the call site instead of in the function itself.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102350","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=102350"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/102350\/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=102350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=102350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=102350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}