{"id":106288,"date":"2022-02-24T07:00:00","date_gmt":"2022-02-24T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106288"},"modified":"2022-02-24T06:50:26","modified_gmt":"2022-02-24T14:50:26","slug":"20220224-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220224-00\/?p=106288","title":{"rendered":"COM asynchronous interfaces, part 9: Asynchronous release, assembling a solution"},"content":{"rendered":"<p>Last time, we learned about <a title=\"COM asynchronous interfaces, part 8: Asynchronous release, the problems\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220223-00\/?p=106282\"> the complex juggling required in order to accomplish a successful asynchronous release<\/a>. Let&#8217;s try to put them together.<\/p>\n<p>One of the things we need to do is aggregate the call object so that we can learn when the call has completed. This tells us when it&#8217;s safe to call <code>Finish_<wbr \/>Release<\/code> and complete the client-side portion of the operation.<\/p>\n<pre>struct SyncForRelease :\r\n    winrt::implements&lt;SyncForRelease, ISynchronize&gt;\r\n{\r\n  winrt::com_ptr&lt;::IUnknown&gt; m_inner;\r\n  <span style=\"color: blue;\">::AsyncIUnknown* m_asyncUnknown;<\/span>\r\n\r\n  int32_t query_interface_tearoff(winrt::guid const&amp; id, void** object)\r\n    const noexcept override {\r\n    if (m_inner) return m_inner.as(id, object);\r\n    return E_NOINTERFACE;\r\n  }\r\n\r\n  auto Sync() <span style=\"color: blue;\">noexcept<\/span> { return m_inner.as&lt;ISynchronize&gt;(); }\r\n\r\n  STDMETHODIMP Reset() { return Sync()-&gt;Reset(); }\r\n  STDMETHODIMP Signal() {\r\n    auto hr = Sync()-&gt;Signal();\r\n    <span style=\"color: blue;\">m_asyncUnknown-&gt;Finish_Release();\r\n    m_inner.detach(); \/\/ don't Release it\r\n    Release(); \/\/ I am dead to me<\/span>\r\n    return hr;\r\n  }\r\n  STDMETHODIMP Wait(DWORD flags, DWORD timeout) {\r\n    return Sync()-&gt;Wait(flags, timeout);\r\n  }\r\n};\r\n<\/pre>\n<p>This is the object we&#8217;re going to use to aggregate the call. This follow the pattern we had seen earlier for aggregating the call object in order to override the <code>ISynchronize<\/code> method, and doing out bonus work inside the <code>Signal<\/code> method.<\/p>\n<p>Actually, if the <code>Signal<\/code> and <code>Wait<\/code> calls fail, we fail to clean up or fail to wait for the operation to complete, and we have nowhere to report the failure. We may as well just fail fast. Instead of trying to catch the exception coming from the <code>Sync()<\/code> method, I just mark it as <code>noexcept<\/code>, which terminates the process if the query fails.<\/p>\n<p>The stuff we do in the <code>Signal<\/code> won&#8217;t make sense until we understand how things are set up. So let&#8217;s set them up:<\/p>\n<pre>void ReleaseAsynchronously(IUnknown* unk)\r\n{\r\n  winrt::com_ptr&lt;::ICallFactory&gt; factory;\r\n  unk-&gt;QueryInterface(IID_PPV_ARGS(factory.put()));\r\n  unk-&gt;Release();\r\n  if (!factory) return;\r\n\r\n  winrt::com_ptr&lt;SyncForRelease&gt; sync;\r\n  try {\r\n    sync = winrt::make_self&lt;SyncForRelease&gt;();\r\n  } catch (std::bad_alloc const&amp;) { }\r\n  if (!sync) return;\r\n\r\n  factory-&gt;CreateCall(\r\n    __uuidof(::AsyncIUnknown), sync.get(),\r\n    __uuidof(::IUnknown), sync-&gt;m_inner.put());\r\n  factory = nullptr;\r\n  if (!sync-&gt;m_inner) return;\r\n\r\n  sync-&gt;m_inner.as(IID_PPV_ARGS(&amp;sync-&gt;m_asyncUnknown));\r\n  if (!sync-&gt;m_asyncUnknown) return;\r\n\r\n  \/\/ Release + AddRef cancel out\r\n\r\n  sync-&gt;m_asyncUnknown-&gt;Begin_Release();\r\n}\r\n<\/pre>\n<p>This function guarantees that the incoming <code>IUnknown<\/code> is released, one way or another: If we can&#8217;t release it asynchronously, then we&#8217;ll release it synchronously. This makes things easier for the caller, who can treat it as a fire-and-forget type of function.<\/p>\n<p>First, we query the <code>IUnknown<\/code> for <code>ICallFactory<\/code>, and then immediately release the <code>IUnknown<\/code>. If the object is local, then the query will fail, and the <code>Release<\/code> will be a synchronous one. We detect this failure and return: The object has been release synchronously, and we&#8217;re done.<\/p>\n<p>If the query for <code>ICallFactory<\/code> succeeds, then we have a proxy to a remote object. The release of the <code>IUnknown<\/code> won&#8217;t destroy the proxy because the <code>ICallFactory<\/code> is still outstanding.<\/p>\n<p>Next up, we create the <code>Sync\u00adFor\u00adRelease<\/code> object, which we will use to aggregate the call so that we can be called back when the asynchronous method completes. We do it inside of a <code>try<\/code> block so we can handle the low-memory case and abandon the operation. The <code>return<\/code> will release the factory, which will be a synchronous release of the proxy. Sorry, we tried.<\/p>\n<p>Assuming we have the <code>Sync\u00adFor\u00adRelease<\/code> object, we ask the factory to create a call (saving it as the aggregated inner object), and then immediately release the factory. This is a repeat of the previous pattern: If the <code>Create\u00adCall<\/code> fails, then the release of the factory is a synchronous release, and we just return immediately. Otherwise, we keep going.<\/p>\n<p>We ask the aggregated inner object for <code>AsyncIUnknown<\/code> so we can call the <code>Begin_<wbr \/>Release<\/code> and <code>Finish_<wbr \/>Release<\/code> methods. Again, if this fails, we just return, and the destructors will release the proxy synchronously.<\/p>\n<p>Now, the normal pattern for querying an inner object for an interface is to perform the <code>Query\u00adInterface<\/code>, and then perform a counteracting <code>Release<\/code> on the outer object. So in theory, there should be a <code>Release()<\/code> call here.<\/p>\n<p>But we have a trick up our sleeve.<\/p>\n<p>The next step would normally be to call <code>AddRef()<\/code> on the <code>Sync\u00adFor\u00adRelease<\/code> object so that it keeps itself alive while the call is in flight. This <code>AddRef()<\/code> cancels out the <code>Release()<\/code>, so the net result is that we don&#8217;t have to do anything! We are basically repurposing the reference count created by the <code>Query\u00adInterface<\/code> call.<\/p>\n<p>Now that everything is set up, we perform the <code>Begin_<wbr \/>Release()<\/code>, which sets the asynchronous call into motion.<\/p>\n<p>And then we wait.<\/p>\n<p>Eventually, the asynchronous call completes, and the <code>Sync\u00adFor\u00adRelease::<wbr \/>Signal<\/code> method is called. After asking the inner object to do the standard signaling work, we proceed with our custom response to the signal. We start by calling <code>Finish_<wbr \/>Release<\/code>, which tells the call object that we have acknowledged the release of the object, and once <code>Finish_<wbr \/>Release<\/code> returns, the object is truly released. The call to <code>Finish_<wbr \/>Release<\/code> will not block because we forwarded the <code>Signal<\/code> call to the inner object, so the call is definitely complete.<\/p>\n<p>When <code>Finish_<wbr \/>Release<\/code> returns, the call object has been released, so we must throw away our references to it without calling <code>Release<\/code>. For our raw pointer, we can just abandon it. For our <code>m_inner<\/code> smart pointer, we use <code>detach()<\/code> to take ownership of the pointer. We just throw the pointer away, because it has already been released by the call to <code>Finish_<wbr \/>Release()<\/code>.<\/p>\n<p>It took us a long time to get here, but we finally got it: A function for asynchronously releasing a COM pointer to a remote object.<\/p>\n<p><b>Bonus chatter<\/b>: Note in particular that our call to <code>Sync()-&gt;Signal()<\/code> was done with a temporary reference to the inner <code>ISynchronize<\/code>, so it got released when <code>Signal()<\/code> returned. If you do some tweaking of this method, make sure that you release the inner <code>ISynchronize<\/code> before calling <code>Finish_<wbr \/>Release()<\/code>. Because <code>Finish_<wbr \/>Release()<\/code> tears down the inner object, and all references to it become dead.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Juggling the references in the right way.<\/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-106288","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Juggling the references in the right way.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106288","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=106288"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106288\/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=106288"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106288"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106288"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}