{"id":106279,"date":"2022-02-22T07:00:00","date_gmt":"2022-02-22T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=106279"},"modified":"2022-02-22T07:32:12","modified_gmt":"2022-02-22T15:32:12","slug":"20220222-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220222-00\/?p=106279","title":{"rendered":"COM asynchronous interfaces, part 7: Being called directly when the operation completes"},"content":{"rendered":"<p>Last time, we learned how we could <a title=\"COM asynchronous interfaces, part 6: Learning about completion without polling\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20220221-42\/?p=106275\"> wait on the internal event handle that is signaled when an asynchronous call completes<\/a>. But that&#8217;s still an indirect discovery of completion. You can register a threadpool wait on the handle, but when your wait callback runs, it&#8217;s running on the threadpool, and if you were operating in a single-threaded apartment, you&#8217;ll have to get control back into that apartment (say by using an <code>IContext\u00adCallback<\/code>).<\/p>\n<p>But there&#8217;s a way to tell the COM marshaling infrastructure to call you back directly, and it even respects your object&#8217;s agility, so if you want to run code in the original apartment, you can do that by providing a non-agile object.<\/p>\n<p>The way to ask for a direct callback is to <i>aggregate<\/i> the call object into your custom outer object. Effectively, you <i>become<\/i> the call object through the magic of aggregation, so all the things that normally are done to the call object are instead done to <i>you<\/i>.<\/p>\n<p>The COM infrastructure uses the <code>ISynchronize<\/code> interface to communicate the state of the call to the call object. If you aggregate the call object, you can take over the responsibilities of <code>ISynchronize<\/code>.<\/p>\n<p>The <code>ISynchronize<\/code> interface models a kernel event handle. The methods are called as follows:<\/p>\n<ul>\n<li><code>ISynchronize::Reset<\/code>: COM calls this method when the asynchronous call starts. The idea is that it&#8217;s resetting the kernel event, to indicate that the call has not completed.<\/li>\n<li><code>ISynchronize::Signal<\/code>: COM calls this method when the asynchronous call completes. The idea is that it&#8217;s setting the kernel event, to indicate that the call is now complete.<\/li>\n<li><code>ISynchronize::Wait<\/code>: COM calls this method when the client calls the <code>Finish_<\/code> method, indicating that it wants to wait for the call to complete (if it hasn&#8217;t completed already). When the <code>Wait<\/code> method returns, COM assumes that the call has completed and returns the answer that was saved in the call object.<\/li>\n<\/ul>\n<p>You can substitute any other object that follows this same pattern. You don&#8217;t even have to have a real kernel object. You just need something that can <i>pretend<\/i> to be a kernel object enough to satisfy the <code>ISynchronize<\/code> contract.<\/p>\n<pre>struct MySynchronize : winrt::implements&lt;MySynchronize, ::ISynchronize&gt;\r\n{\r\n  winrt::com_ptr&lt;::IUnknown&gt; m_inner;\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  wil::slim_event ready;\r\n\r\n  STDMETHODIMP Reset() { ready.ResetEvent(); return S_OK; }\r\n  STDMETHODIMP Signal() { ready.SetEvent();\r\n    printf(\"Call completed!\\n\"); \/\/ do cool stuff here\r\n    return S_OK; }\r\n  STDMETHODIMP Wait(DWORD flags, DWORD timeout) {\r\n    assert(is_mta()); \/\/ we won't be pumping messages\r\n    assert(!(flags &amp; COWAIT_ALERTABLE)); \/\/ we won't be waiting alertably\r\n    return ready.wait(timeout) ? S_OK : RPC_S_CALLPENDING;\r\n  }\r\n\r\n  static bool is_mta() {\r\n    APTTYPE type;\r\n    APTTYPEQUALIFIER qualifier;\r\n    THROW_IF_FAILED(CoGetApartmentType(&amp;type, &amp;qualifier));\r\n    return type == APTTYPE_MTA;\r\n  }\r\n};\r\n<\/pre>\n<p>The <code>My\u00adSynchronize<\/code> class starts with one of the common aggregation outer object patterns: It has an inner object (<code>m_inner<\/code>), and we want to aggregate all the interfaces of the inner object. Therefore, our custom <code>query_<wbr \/>interface_<wbr \/>tearoff<\/code> method forwards <i>all<\/i> interface queries to the inner object.<\/p>\n<p>After that comes our custom implementation of <code>ISynchronize<\/code>. Our version doesn&#8217;t use a real kernel object. It uses the lightweight event-like object built out of <code>Wait\u00adOn\u00adAddress<\/code> as provided by the Windows Implementation Library.<\/p>\n<p>One of the tricky parts here is the <code>Wait<\/code> method: Most of the flags relate to how the method should wait if running on an STA. We don&#8217;t want to deal with any of that nonsense, so we just decide not to support them, nor do we support alertable waits.<\/p>\n<p>Mind you, this decision not to support STA or alertable waits needs to be done in coordination with the clients of the call object. But if you yourself are the client, then you know whether you ever use it from an STA or with an alertable wait. (COM always calls with <code>COWAIT_<wbr \/>DEFAULT<\/code> from the thread that called the <code>Finish_<\/code> method.)<\/p>\n<p>A simpler way is to delegate the <code>ISynchronize<\/code> methods back to the call object:<\/p>\n<pre>struct MySynchronize :\r\n    winrt::implements&lt;MySynchronize, ::ISynchronize, winrt::non_agile&gt;\r\n{\r\n  winrt::com_ptr&lt;::IUnknown&gt; m_inner;\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() { 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 = return Sync()-&gt;Signal();\r\n    printf(\"Call completed!\\n\"); \/\/ do cool stuff here\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>Let&#8217;s take this out for a spin.<\/p>\n<pre>int main(int, char**)\r\n{\r\n  winrt::init_apartment(winrt::apartment_type::multi_threaded);\r\n\r\n  auto pipe = CreateSlowPipeOnOtherThread();\r\n\r\n  <span style=\"color: blue;\">auto outer = winrt::make_self&lt;MySynchronize&gt;();<\/span>\r\n  auto factory = pipe.as&lt;ICallFactory&gt;();\r\n  winrt::check_hresult(factory-&gt;CreateCall(\r\n    __uuidof(::AsyncIPipeByte), <span style=\"color: blue;\">winrt::get_unknown(*outer)<\/span>,\r\n    __uuidof(<span style=\"color: blue;\">::IUnknown<\/span>), <span style=\"color: blue;\">outer-&gt;m_inner.put()<\/span>));\r\n  <span style=\"color: blue;\">auto call = outer.as&lt;::AsyncIPipeByte&gt;();<\/span>\r\n\r\n  printf(\"Beginning the Pull\\n\");\r\n  winrt::check_hresult(call-&gt;Begin_Pull(100));\r\n\r\n  printf(\"Doing something else for a while...\\n\");\r\n  Sleep(100);\r\n\r\n  printf(\"Getting the answer\\n\");\r\n  BYTE buffer[100];\r\n  ULONG actual;\r\n  winrt::check_hresult(call-&gt;Finish_Pull(buffer, &amp;actual));\r\n  printf(\"Pulled %lu bytes\\n\", actual);\r\n\r\n  return 0;\r\n}\r\n<\/pre>\n<p>When the call completes, the <code>ISynchronize::<wbr \/>Signal<\/code> method on the outer object is called, and we can take that opportunity to do some work. Our <code>My\u00adSynchronize<\/code> object is marked as non-agile, so this call is made in the same apartment in which it was created, which is convenient if the <code>Signal<\/code> method wants to access other objects with apartment affinity.<\/p>\n<p>Note that we forward the call into the inner object first, before doing our work. That way, our work is done while the event is signaled. If we didn&#8217;t do that, then if the work calls <code>Finish_<\/code> to get the results of the call that just completed, it will deadlock because the <code>Finish_<\/code> is going to wait for the call to be signaled as complete.<\/p>\n<p>So there&#8217;s a practical use for COM aggregation: It lets you become part of another object and respond to its methods.<\/p>\n<p><b>Bonus chatter<\/b>: I cheated a bit and used a throwing method when forwarding the <code>ISynchronize<\/code> methods. COM methods are not allowed to throw C++ exceptions (because C++ exceptions are not part of the ABI), so we need to convert them back to <code>HRESULT<\/code>s.<\/p>\n<pre>struct MySynchronize :\r\n    winrt::implements&lt;MySynchronize, ::ISynchronize, winrt::non_agile&gt;\r\n{\r\n  winrt::com_ptr&lt;::IUnknown&gt; m_inner;\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() { return m_inner.as&lt;ISynchronize&gt;(); }\r\n\r\n  STDMETHODIMP Reset() <span style=\"color: blue;\">try<\/span> { return Sync()-&gt;Reset(); }\r\n    <span style=\"color: blue;\">catch (...) { return winrt::to_hresult(); }<\/span>\r\n  STDMETHODIMP Signal() <span style=\"color: blue;\">try<\/span> {\r\n    auto hr = return Sync()-&gt;Signal();\r\n    printf(\"Call completed!\\n\"); \/\/ do cool stuff here\r\n    return hr;\r\n  } <span style=\"color: blue;\">catch (...) { return winrt::to_hresult(); }<\/span>\r\n  STDMETHODIMP Wait(DWORD flags, DWORD timeout) <span style=\"color: blue;\">try<\/span> {\r\n    return Sync()-&gt;Wait(flags, timeout);\r\n  } <span style=\"color: blue;\">catch (...) { return winrt::to_hresult(); }<\/span>\r\n};\r\n<\/pre>\n<p><b>Bonus bonus chatter<\/b>: Note how this differs from <i>containment<\/i>, which is the more usual pattern for combining objects. If the outer object <i>contained<\/i> a call object, then queries on the call object would be satisfied by the call object. The outer object never gets a chance to take over the <code>ISynchronize<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Becoming part of the system.<\/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-106279","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Becoming part of the system.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106279","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=106279"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/106279\/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=106279"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=106279"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=106279"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}