{"id":104582,"date":"2020-12-22T07:00:00","date_gmt":"2020-12-22T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=104582"},"modified":"2020-12-24T07:05:05","modified_gmt":"2020-12-24T15:05:05","slug":"20201222-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201222-00\/?p=104582","title":{"rendered":"How can I emulate the REG_NOTIFY_THREAD_AGNOSTIC flag on systems that don&#8217;t support it? part 2"},"content":{"rendered":"<p>We continue our exercise of <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20201221-00\/?p=104574\"> emulating the <code>REG_<wbr \/>NOTIFY_<wbr \/>THREAD_<wbr \/>AGNOSTIC<\/code> flag<\/a>, this time for the case where we don&#8217;t have a readily-available persistent thread. In that case, we can use one from the thread pool, but it&#8217;ll be a bit trickier because we&#8217;ll have to wait for the thread pool work item to run in order to get the result.<\/p>\n<pre>struct RegNotifyChangeKeyValueAsyncArgs\r\n{\r\n  HKEY hkey;\r\n  BOOL bWatchSubtree;\r\n  DWORD dwNotifyFilter;\r\n  HANDLE hEvent;\r\n  LONG result;\r\n  HANDLE hComplete;\r\n\r\n  ~RegNotifyChangeKeyValueAsyncArgs()\r\n  {\r\n    if (hComplete) CloseHandle(hComplete);\r\n  }\r\n};\r\n\r\nDWORD CALLBACK RegNotifyChangeKeyValueOnPersistentThread(\r\n    void* param)\r\n{\r\n  auto args = reinterpret_cast&lt;\r\n               RegNotifyChangeKeyValueAsyncArgs*&gt;(param);\r\n  args-&gt;result = RegNotifyChangeKeyValue(\r\n    args-&gt;hkey,\r\n    args-&gt;bWatchSubtree,\r\n    args-&gt;dwNotifyFilter,\r\n    args-&gt;hEvent,\r\n    TRUE);\r\n  SetEvent(args-&gt;hComplete);\r\n  return 0;\r\n}\r\n\r\n\/\/ See discussion before using this function.\r\nLONG RegNotifyChangeKeyValueAsync(\r\n  HKEY hkey,\r\n  BOOL bWatchSubtree,\r\n  DWORD dwNotifyFilter,\r\n  HANDLE hEvent)\r\n{\r\n  RegNotifyChangeKeyValueAsyncArgs args = \r\n    { hkey, bWatchSubtree, dwNotifyFilter, hEvent,\r\n      ERROR_INVALID_PARAMETER,\r\n      CreateEvent(nullptr, TRUE, FALSE, nullptr) };\r\n  if (!args.hComplete) {\r\n    return static_cast&lt;LONG&gt;(GetLastError());\r\n  }\r\n  if (!QueueUserWorkItem(\r\n          RegNotifyChangeKeyValueOnPersistentThread,\r\n          &amp;args,\r\n          WT_EXECUTEINPERSISTENTTHREAD)) {\r\n    return static_cast&lt;LONG&gt;(GetLastError());\r\n  }\r\n  WaitForSingleObject(args-&gt;hComplete, INFINITE);\r\n  return args.result;\r\n}\r\n<\/pre>\n<p>One of the tricks of this exercise is limiting the solution to the features that were available in Windows XP.<\/p>\n<p>Since we need to do the registration from a thread that will remain running indefinitely, we ask the thread pool for help by calling <code>Queue<wbr \/>User<wbr \/>Work<wbr \/>Item<\/code> with the <code>WT_<wbr \/>EXECUTE\u00adIN\u00adPERSISTENT\u00adTHREAD<\/code> flag. The callback runs on a persistent thread, and it is there that we call <code>Reg\u00adNotify\u00adChange\u00adKey\u00adValue<\/code> and pass the result back. Meanwhile, the main thread waits for the work item to finish so it can propagate the results to the caller.<\/p>\n<p>This design seems to work, but it has a trap: If the caller of <code>Reg\u00adNotify\u00adChange\u00adKey\u00adValue\u00adAsync<\/code> is itself on the thread pool, then you end up with the risk of <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20200501-00\/?p=103720\"> starving the thread pool and preventing it from doing the thing that would relieve the starvation<\/a>.<\/p>\n<p>We&#8217;ll look at ways of fixing this next time.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Asking the thread pool for help.<\/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-104582","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Asking the thread pool for help.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104582","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=104582"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/104582\/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=104582"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=104582"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=104582"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}