{"id":110116,"date":"2024-08-09T07:00:00","date_gmt":"2024-08-09T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=110116"},"modified":"2024-08-09T09:18:23","modified_gmt":"2024-08-09T16:18:23","slug":"20240809-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240809-00\/?p=110116","title":{"rendered":"What does it even mean to Close a Windows Runtime asynchronous operation or action?"},"content":{"rendered":"<p>Windows Runtime asynchronous operations and actions support the <code>Close<\/code> method. What does that even mean?<\/p>\n<p>There are at least five different implementations of Windows Runtime asynchronous operations and actions, and each one deals with the <code>Close<\/code> operation differently. Let&#8217;s look at the different implementations and see if we can infer the principle that guides the <code>Close<\/code> method.<\/p>\n<p>C++\/WinRT&#8217;s asynchronous operations and actions have a very simple implementation of <code>Close<\/code>: <a href=\"https:\/\/github.com\/microsoft\/cppwinrt\/blob\/0deecf1ea4fcc6acd10602e2340e243fcdcf4f47\/strings\/base_coroutine_foundation.h#L500\"> It does nothing<\/a>! It doesn&#8217;t even check whether you called it before the asynchronous operation or action has completed.<\/p>\n<p>Okay, so we didn&#8217;t learn much from that.<\/p>\n<p>Next up is an internal implementation used by many Windows components, which builds on top of <a href=\"https:\/\/learn.microsoft.com\/cpp\/cppcx\/wrl\/asyncbase-class?view=msvc-170\"> WRL&#8217;s <code>AsyncBase<\/code><\/a>. The <code>Close<\/code> method discards the result of <code>IAsync\u00adOperation<\/code> if the result is a reference type or a string. If you <code>Close<\/code> one of those guys, then the <code>Get\u00adResults()<\/code> may return an empty result instead of the actual result.<\/p>\n<p>Okay, so one thing we learned is that once you call <code>Close<\/code>, you can&#8217;t call <code>Get\u00adResults()<\/code> and get reliable results.<\/p>\n<p>Furthermore, the implementation of <code>AsyncBase<\/code> throws an &#8220;illegal state change&#8221; exception if you try to <code>Close<\/code> the asynchronous operation or action before it has completed. And any method calls or property accesses after you <code>Close<\/code> throw an &#8220;illegal method call&#8221; exception. So those are some other rules we can remember.<\/p>\n<p>Another implementation <a href=\"https:\/\/github.com\/Microsoft\/Win2D\/blob\/uwp\/main\/winrt\/inc\/AsyncOperation.h\"> ships with Win2D<\/a>. This is also built on top of WRL&#8217;s <code>AsyncBase<\/code>. The Win2D version supports only reference types, and the <code>Close<\/code> method frees the completion result. So its behavior is a subset of the Windows internal implementation. We didn&#8217;t learn anything new here.<\/p>\n<p>A fourth implementation can be found <a href=\"https:\/\/github.com\/microsoft\/WinUI-Samples\/blob\/cc590dc1f4d8c1339ef38a6f14d1f9fba06c3ecb\/Shared\/WinUISplashScreen\/Native\/ppltasks.h\"> in the Parallel Patterns Library (PPL)<\/a>. In this implementation, the <code>Close<\/code> method throws an &#8220;illegal state change&#8221; exception if the asynchronous action or operation has not yet completed. Assuming that the operation has completed, the <code>Close<\/code> method delegates to the virtual <code>_OnClose<\/code> method, but the default implementation of <code>_OnClose<\/code> does nothing. After the asynchronous operation or action has been closed, you cannot ask for its <code>Id<\/code>, <code>ErrorCode<\/code>, <code>Status<\/code>, or <code>Progress<\/code> handler, you cannot set the <code>Completed<\/code> or <code>Progress<\/code> handler, nor can you call <code>GetResults()<\/code>. (Though it lets you <i>read<\/i> the <code>Completed<\/code> and <code>Progress<\/code> properties.)<\/p>\n<p>The fifth implementation is in <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/system.windowsruntimesystemextensions?view=dotnet-plat-ext-3.1\"> the <code>Windows\u00adRuntime\u00adSystem\u00adExtensions<\/code> extension class<\/a>. I don&#8217;t have the source code, but I was able to reverse-engineer it with the help of <a href=\"https:\/\/github.com\/icsharpcode\/ILSpy\"> ILSpy<\/a>. In this implementation, calling <code>Close<\/code> before the asynchronous operation or action has completed throws an exception. If you call it after the completion, then the completion results are freed, as well as the completion handler, the progress handler (if applicable), and any supporting data structures for those things. And after you close the asynchronous operation or action, all future method calls or member accesses throw an exception.<\/p>\n<p>Okay, so combining all of these observations allows us to infer the rules for closing asynchronous operations and actions:<\/p>\n<ul>\n<li>You may not call <code>Close<\/code> until the operation or action has completed.<\/li>\n<li>If you call <code>Close<\/code>, then the operation is permitted (but not required) to release any resources associated with the operation or action.<\/li>\n<li>Once you have <code>Close<\/code>d the operation or action, no method calls or member accesses are permitted.<\/li>\n<\/ul>\n<p>This seems be a reasonable set of rules, and I don&#8217;t see any real opportunities for it to become any stricter, so I think we&#8217;ve found it.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>That&#8217;s the end of the road.<\/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-110116","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>That&#8217;s the end of the road.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110116","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=110116"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/110116\/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=110116"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=110116"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=110116"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}