{"id":104602,"date":"2020-12-25T07:00:00","date_gmt":"2020-12-25T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104602"},"modified":"2020-12-24T07:30:29","modified_gmt":"2020-12-24T15:30:29","slug":"20201225-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201225-00\/?p=104602","title":{"rendered":"How can I emulate the REG_NOTIFY_THREAD_AGNOSTIC flag on systems that don\u2019t support it? part 5"},"content":{"rendered":"<p>We complete our somewhat pointless exercise of <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201224-00\/?p=104599\"> emulating the <code>REG_<wbr \/>NOTIFY_<wbr \/>THREAD_<wbr \/>AGNOSTIC<\/code> flag<\/a> by making our coroutine resilient to failure partway through. This requires you to accept the anachronism of using C++20 coroutines, while also dropping Windows XP support, since we will be relying on thread pool features new to Windows Vista.<\/p>\n<pre>auto RegNotifyChangeKeyValueAsync(\r\n  HKEY hkey,\r\n  BOOL bWatchSubtree,\r\n  DWORD dwNotifyFilter,\r\n  HANDLE hEvent)\r\n{\r\n  struct awaiter\r\n  {\r\n    awaiter(awaiter const&amp;) = delete;\r\n    void operator=awaiter(awaiter const&amp;) = delete;\r\n\r\n    HKEY m_hkey;\r\n    BOOL m_bWatchSubtree;\r\n    DWORD m_dwNotifyFilter;\r\n    HANDLE m_hEvent;\r\n    LONG m_result;\r\n    PTP_WORK m_completionWork = nullptr;\r\n    std::experimental::coroutine_handle&lt;&gt; m_handle;\r\n\r\n    ~awaiter()\r\n    {\r\n      if (m_completionWork) CloseThreadpoolWork(m_completionWork);\r\n    }\r\n\r\n    bool await_ready() const noexcept { return false; }\r\n\r\n    bool await_suspend(std::experimental::coroutine_handle&lt;&gt; handle)\r\n    {\r\n      m_completionWork = CreateThreadpoolWork(Complete, this, nullptr);\r\n      if (!m_completionWork) {\r\n        m_result = static_cast&lt;LONG&gt;(GetLastError());\r\n        return false;\r\n      }\r\n\r\n      m_handle = handle;\r\n\r\n      if (!QueueUserWorkItem(\r\n          Register,\r\n          this,\r\n          WT_EXECUTEINPERSISTENTTHREAD)) {\r\n        m_result = static_cast&lt;LONG&gt;(GetLastError());\r\n        return false;\r\n      }\r\n\r\n      return true;\r\n    }\r\n\r\n    LONG await_ready() const noexcept { return m_result; }\r\n\r\n    DWORD CALLBACK Register(void* param)\r\n    {\r\n      auto self = reinterpret_cast&lt;awaiter*&gt;(param);\r\n      self-&gt;m_result = RegNotifyChangeKeyValue(\r\n        self-&gt;m_hkey,\r\n        self-&gt;m_bWatchSubtree,\r\n        self-&gt;m_dwNotifyFilter,\r\n        self-&gt;m_hEvent,\r\n        TRUE);\r\n      SubmitThreadpoolWork(m_completionWork);\r\n      return 0;\r\n    }\r\n\r\n    DWORD CALLBACK Complete(void* param)\r\n    {\r\n      auto self = reinterpret_cast&lt;awaiter*&gt;(param);\r\n      self-&gt;m_handle();\r\n      return 0;\r\n    }\r\n  };\r\n\r\n  return awaiter(hkey, bWatchSubtree, dwNotifyFilter, hEvent);\r\n}\r\n<\/pre>\n<p>The idea here is that we have two work items. The first (for which we use <code>Queue\u00adUser\u00adWork\u00adItem<\/code>) is scheduled onto a persistent thread. When that first work item runs (<code>Register<\/code>), we register the notification and save the result. And then we submit the second work item, which brings us to a normal thread pool thread, which is where we resume the caller by invoking its coroutine handle.<\/p>\n<p>As before, if anything goes wrong during the set-up, we save the error and declare that the caller shouldn&#8217;t suspend. That way, it picks up the error immediately.<\/p>\n<p>There&#8217;s a subtlety here: You might be tempted to clean up the completion work item as soon as <code>Submit\u00adThreadpool\u00adWork<\/code> returns, but that would be wrong. There is a race condition where the submitted work runs to completion before <code>Submit\u00adThreadpool\u00adWork<\/code> returns. In that case, the coroutine has already resumed, and the <code>awaiter<\/code> has already destructed. The subsequent call to <code>Close\u00adThreadpool\u00adWork(<wbr \/>m_completionWork);<\/code> is accessing an object after it has been destroyed.<\/p>\n<p><b>Bonus chatter<\/b>: Commenter Paul Jackson <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201223-00\/?p=104584#comment-137566\"> observed that<\/a> the thread which executes <code>WT_<wbr \/>EXECUTE\u00adIN\u00adPERSISTENT\u00adTHREAD<\/code> work items can exit if there are no pending I\/O requests. Is RegNotifyChangeKeyValue a pending I\/O request? I&#8217;m not sure. So maybe <code>WT_<wbr \/>EXECUTE\u00adIN\u00adPERSISTENT\u00adTHREAD<\/code> doesn&#8217;t solve the problem after all. Fortunately, this was all a pointless exercise.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Using the new thread pool functions.<\/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-104602","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Using the new thread pool functions.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104602","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=104602"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104602\/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=104602"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104602"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104602"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}