{"id":107790,"date":"2023-02-03T07:00:00","date_gmt":"2023-02-03T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=107790"},"modified":"2023-02-03T07:27:53","modified_gmt":"2023-02-03T15:27:53","slug":"20230203-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20230203-00\/?p=107790","title":{"rendered":"Inside C++\/WinRT: Coroutine completions: Cancellation propagation"},"content":{"rendered":"<p>If you enable <a title=\"How to get your C++\/WinRT asynchronous operations to respond more quickly to cancellation, part 3\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200923-00\/?p=104261\"> C++\/WinRT cancellation propagation<\/a>, and somebody asks to cancel your coroutine while you are awaiting another coroutine, the propagation infrastructure will try to cancel the coroutine you are awaiting.<\/p>\n<p>We add support for propagating cancellation into a Windows Runtime asynchronous operation, we have our awaiter derive from <code>winrt::<wbr \/>enable_<wbr \/>await_<wbr \/>cancellation<\/code> and implementing a canceller.<\/p>\n<pre>template &lt;typename Async&gt;\r\nstruct await_adapter <span style=\"color: #08f;\">: enable_await_cancellation<\/span>\r\n{\r\n    \u301a ... \u301b\r\n\r\n    <span style=\"color: #08f;\">void enable_cancellation(cancellable_promise* promise)\r\n    {\r\n        promise-&gt;set_canceller([](void* parameter)\r\n        {\r\n            cancel_asynchronously(\r\n                reinterpret_cast&lt;await_adapter*&gt;(parameter)\r\n                    -&gt;async);\r\n        }, this);\r\n    }<\/span>\r\n\r\n    \u301a ... \u301b\r\n\r\n    <span style=\"color: #08f;\">static fire_and_forget cancel_asynchronously(Async async)\r\n    {\r\n        co_await winrt::resume_background();\r\n        try\r\n        {\r\n            async.Cancel();\r\n        }\r\n        catch (hresult_error const&amp;)\r\n        {\r\n        }\r\n    }<\/span>\r\n    \u301a ... \u301b\r\n};\r\n<\/pre>\n<p>To support cancellation, we derive from <code>winrt::<wbr \/>enable_<wbr \/>await_<wbr \/>cancellation<\/code> and implement the <code>enable_<wbr \/>cancellation<\/code> method. This method is given a <code>cancellable_<wbr \/>promise<\/code>, and its job is to call the <code>set_<wbr \/>canceller<\/code> method to tell the promise how to cancel this awaitable. The information takes the form of a function pointer (usually a captureless lambda that converts to a function pointer) and a context pointer (usually <code>this<\/code>).<\/p>\n<p>If the coroutine is an <code>IAsyncAction<\/code> or <code>IAsyncOperation<\/code>, then it will support a <code>Cancel()<\/code> method, and upon cancellation, the C++\/WinRT infrastructure will call the canceller to cancel the thing being awaited.<\/p>\n<p>If we are awaiting another Windows Runtime asynchronous operation, what our canceller does is hop to a background thread and then try to cancel the thing we are awaiting. This is a best-effort operation, so we ignore any errors. (For example, we could encounter a race condition where the operation completes just as we&#8217;re about to cancel it.)<\/p>\n<p>Note that cancellers can be called multiple times, so if you&#8217;re writing your own custom canceller, make sure it is harmless to call a second time.<\/p>\n<p>Supporting cancellation is a bit awkward because it is designed for efficiency: Cancellation is extremely rare, so we want to make preparing for cancellation cheap, and are okay with the actual work of cancellation being expensive.<\/p>\n<p>Okay, that was a whirlwind tour of how C++\/WinRT implements <code>co_await<\/code> for Windows Runtime asynchronous operations. It&#8217;s not that complicated, but it looks intimidating when broken down into pieces.\u00b9<\/p>\n<p>The overall goal of the &#8220;Inside C++\/WinRT&#8221; series is to share the knowledge of how C++\/WinRT works, so that more people can pitch in when somebody has a question.<\/p>\n<p>\u00b9 It&#8217;s similar to explaining a game to someone. When you play the game, the rules are simple: &#8220;You hit the ball back and forth, and the loser is the person who fails to hit the ball.&#8221; However, when you try to explain the game to someone else, it becomes surprisingly complicated: &#8220;Well, except you don&#8217;t have to hit the ball under these conditions, and sometimes hits don&#8217;t count, and sometimes you&#8217;re allowed to hit twice, and, gosh, now that I&#8217;m explaining it in detail, it doesn&#8217;t sound simple at all.&#8221;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Giving up.<\/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-107790","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Giving up.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107790","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=107790"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/107790\/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=107790"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=107790"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=107790"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}