{"id":106282,"date":"2022-02-23T07:00:00","date_gmt":"2022-02-23T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106282"},"modified":"2022-02-23T07:40:20","modified_gmt":"2022-02-23T15:40:20","slug":"20220223-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220223-00\/?p=106282","title":{"rendered":"COM asynchronous interfaces, part 8: Asynchronous release, the problems"},"content":{"rendered":"<p>It is usually the case that when you release a COM object, you don&#8217;t particularly care what happens to the object afterward. There are some cases where you do care, usually when there is some cleanup activity associated with the final release that you are relying upon. But usually, you are declaring lack of interest in the future activities of the object when you release.<\/p>\n<p>By default, the <code>Release<\/code> method is synchronous. If the object is remote, then the final\u00b9 release request goes out to the server, and you sit and wait until the server replies that yes, it has definitely released your object. I&#8217;ve run into cases where my code has hung because I&#8217;m cleaning up some object that has a reference to a remote object, and the release of the remote object is hung because the server has stopped responding. I really don&#8217;t care about knowing when the release is completed and would be happy to let the release occur asynchronously. Can you release asynchronously?<\/p>\n<p>Yes, you can release asynchronously. It roughly follows the same pattern we&#8217;ve been using so far, with <code>IUnknown<\/code> as the interface being run asynchronously. However, <code>AsyncIUnknown<\/code> requires extra care because you&#8217;re messing with the underlying object lifetimes. I&#8217;ll guide you through the treacherous waters.<\/p>\n<p>Let&#8217;s make our <code>Slow\u00adPipe<\/code> even slower by adding a delay to its destructor.<\/p>\n<pre>struct SlowPipe :\r\n    winrt::implements&lt;SlowPipe, ::IPipeByte, winrt::non_agile&gt;\r\n{\r\n  \/\/ exit the STA thread when we destruct\r\n  ~SlowPipe() {\r\n    <span style=\"color: blue;\">Sleep(2000);\r\n    printf(\"Finally destroyed\\n\");<\/span>\r\n    PostQuitMessage(0);\r\n  }\r\n\r\n  ...\r\n};\r\n<\/pre>\n<p>We can avoid this slow <code>Release<\/code> by making the release asynchronous. Our initial impulse is to follow the general pattern for asynchronous calls:<\/p>\n<pre>\/\/ Don't use this code. See text.\r\nint main(int, char**)\r\n{\r\n  winrt::init_apartment(winrt::apartment_type::multi_threaded);\r\n\r\n  {\r\n    auto pipe = CreateSlowPipeOnOtherThread();\r\n\r\n    winrt::com_ptr&lt;::<span style=\"color: blue;\">AsyncIUnknown<\/span>&gt; call;\r\n    auto factory = pipe.as&lt;ICallFactory&gt;();\r\n    winrt::check_hresult(factory-&gt;CreateCall(\r\n      __uuidof(::<span style=\"color: blue;\">AsyncIUnknown<\/span>), nullptr,\r\n      __uuidof(::<span style=\"color: blue;\">AsyncIUnknown<\/span>),\r\n      reinterpret_cast&lt;::IUnknown**&gt;(call.put())));\r\n\r\n    winrt::check_hresult(call-&gt;Begin_Release());\r\n\r\n    \/\/ force all objects to destruct, to prove we're done\r\n  }\r\n  printf(\"Getting on with our life.\\n\");\r\n\r\n  Sleep(5000); \/\/ just so we can see the release complete\r\n\r\n  return 0;\r\n}\r\n<\/pre>\n<p>We are following the general asynchronous pattern: Get the call factory, create an asynchronous call for <code>IUnknown<\/code>, begin the <code>Release<\/code>, and then throw it all away, to indicate that you are not interested in the result.<\/p>\n<p>Unfortunately, this doesn&#8217;t work.<\/p>\n<p>The first mistake is failing to keep track of all the outstanding references. At the time we call <code>Begin_<wbr \/>Release<\/code>, there are three outstanding references to the object: One in <code>pipe<\/code>, another in <code>factory<\/code>, and a third in <code>call<\/code>. That <code>Begin_<wbr \/>Release<\/code> is not going to be the final release, so it&#8217;s just going to decrement the local reference count in the proxy, and nothing will go out over the wire to the remote object. And then when we get around to cleaning up and releasing <code>pipe<\/code>, that&#8217;s the one that releases the final reference in the proxy, and that&#8217;s the one that triggers a call to the remote object.<\/p>\n<p>That call is a synchronous call.<\/p>\n<p>So we need to make sure that the only remaining reference to the remote object is in the call object. We can do that by releasing the <code>pipe<\/code> and <code>factory<\/code> references early, prior to their natural destruction.<\/p>\n<p>The next problem is that we performed a <code>Begin_<wbr \/>Release<\/code> to initiate an asynchronous <code>Release<\/code> operation, and then the <code>call<\/code> object destructs, which performs its own <code>Release<\/code>. We&#8217;re performing a double-release of that last reference: One release is asynchronous (explicit call to <code>Begin_<wbr \/>Release<\/code>) and the other is synchronous (implicit <code>Release<\/code> at destruction).<\/p>\n<p>Okay, so instead of allowing <code>call<\/code> to destruct naturally, we need to perform a <code>detach<\/code> operation to remove control of the call from the <code>call<\/code> variable. In this case, we&#8217;re telling the <code>call<\/code> variable, &#8220;Don&#8217;t worry, I&#8217;ll take care of it.&#8221; And we took care of it by asking for the release to happen asynchronously.<\/p>\n<p>But things are still not quite right.<\/p>\n<p>You see, the usual pattern of throwing away a call doesn&#8217;t work for <code>IUnknown::<wbr \/>Release<\/code>: Under the usual pattern, the call object normally discovers whether you plan on calling <code>Finish_<\/code> by observing that you released the object. But when we use the async pattern for <code>IUnknown::<wbr \/>Release<\/code>, we just throw away the call object <i>without even calling <code>Release<\/code><\/i>. This leaves the call object in a pickle: &#8220;Should I remain valid so the caller can call <code>Finish_<wbr \/>Release<\/code>? Or should I just clean up right away?&#8221;<\/p>\n<p>The call object for <code>AsyncIUnknown<\/code> plays it safe and assumes you want to call <code>Finish_<wbr \/>Release<\/code>. But that means you now <i>must<\/i> call <code>Finish_<wbr \/>Release<\/code>.<\/p>\n<p>But wait, calling <code>Finish_<wbr \/>Release<\/code> means that we block until the <code>Release<\/code> completes. That brings us back full circle: Our attempt at an asynchronous <code>Release<\/code> resulted in a synchronous wait!<\/p>\n<p>The solution here is to aggregate the call so we can be notified via <code>ISynchronize::<wbr \/>Signal<\/code> that the call has completed. At that point, we call <code>Finish_<wbr \/>Release<\/code> to complete the call.<\/p>\n<p>Putting this all together will require us to apply a lot of what we&#8217;ve learned about COM aggregation. We&#8217;ll set to work next time.<\/p>\n<p>\u00b9 Only the final release request goes over the wire. Non-final releases merely decrement the reference count of the local proxy.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you don&#8217;t need something any more, then you usually don&#8217;t really care when it gets taken away.<\/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-106282","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>If you don&#8217;t need something any more, then you usually don&#8217;t really care when it gets taken away.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106282","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=106282"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106282\/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=106282"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106282"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}