{"id":109438,"date":"2024-02-23T07:00:00","date_gmt":"2024-02-23T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109438"},"modified":"2024-02-23T09:52:44","modified_gmt":"2024-02-23T17:52:44","slug":"20240223-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240223-00\/?p=109438","title":{"rendered":"Gotcha: Be careful how you shut down your dispatcher queues"},"content":{"rendered":"<p>Last time, <a title=\"Gotcha: Don't forget to shut down your dispatcher queues\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240222-11\/?p=109434\"> we learned that it is important to shut down your dispatcher queues<\/a>. This is done by calling <code>Dispatcher\u00adQueue\u00adController.<wbr \/>Shutdown\u00adQueue\u00adAsync()<\/code>. The operation completes after the queue has shut down and its thread has become useless. But there&#8217;s a catch.<\/p>\n<p>The catch is the case where you await a call to <code>Dispatcher\u00adQueue\u00adController.<wbr \/>Shutdown\u00adQueue\u00adAsync()<\/code> <i>from the dispatcher queue thread<\/i>. In this case, you are asking the current thread to shut itself down, and then expecting to regain control on the very thread that has been shut down.<\/p>\n<p>You are now skating on thin ice.<\/p>\n<pre>namespace winrt\r\n{\r\n    using namespace winrt::Windows::Foundation;\r\n    using namespace winrt::Windows::System;\r\n}\r\n\r\nwinrt::IAsyncAction Example()\r\n{\r\n    \/\/ Create a dispatcher queue controller\r\n    \/\/ for a new dispatcher queue thread\r\n    auto controller =\r\n        winrt::DispatcherQueueController::CreateOnDedicatedThread();\r\n\r\n    \/\/ To demonstrate the problem, switch to the dispatcher queue thread\r\n    co_await winrt::resume_foreground(controller.DispatcherQueue());\r\n\r\n    \/\/ Shut down the dispatcher queue while we're on its thread (!)\r\n    co_await controller.ShutdownQueueAsync();\r\n}\r\n<\/pre>\n<p>Both C# and C++\/WinRT have a default policy of resuming execution after an <code>await<\/code> \/ <code>co_await<\/code> in the same COM apartment in which the await started.\u00b9 If you start the await on the dispatcher queue thread, then execution resumes on a dispatcher queue thread that has already shut down and is about to exit.<\/p>\n<p>Even though the thread is about to exit, it is still a single-threaded COM apartment (at least for a little while longer), so any <code>await<\/code> \/ <code>co_await<\/code> \/ PPL task is going to try to return to that single-thread COM apartment which has been destroyed. Furthermore, any operations you perform that involve multiple threads may fail because they no longer have a way to get back to the dispatcher queue thread.<\/p>\n<pre>winrt::IAsyncAction Example()\r\n{\r\n    \/\/ Create a dispatcher queue controller\r\n    \/\/ for a new dispatcher queue thread\r\n    auto controller =\r\n        winrt::DispatcherQueueController::CreateOnDedicatedThread();\r\n\r\n    \/\/ To demonstrate the problem, switch to the dispatcher queue thread\r\n    co_await winrt::resume_foreground(controller.DispatcherQueue());\r\n\r\n    \/\/ Shut down the dispatcher queue while we're on its thread (!)\r\n    co_await controller.ShutdownQueueAsync();\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Oh no, this will try to resume on the dispatcher queue thread<\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ after it has been shut down.                                 <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">co_await winrt::Launcher::LaunchUriAsync(                       <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">    winrt::Uri{L\"https:\/\/microsoft.com\"});                      <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                               <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">DoSomeMoreStuff();                                              <\/span>\r\n}\r\n<\/pre>\n<p>That call to <code>Launch\u00adUri\u00adAsync<\/code> is in big trouble. After the launch completes, it&#8217;s going to try to return to the dispatcher queue thread, which likely no longer exists by the time it completes. The code will throw an exception representing <code>RPC_<wbr \/>E_<wbr \/>DISCONNECTED<\/code>, and nothing after the <code>co_await<\/code> will execute.<\/p>\n<p>Probably the best way to avoid this problem is to shut down the dispatcher queue from a background thread. That way, you never find yourself running on a thread that is slated for death.<\/p>\n<pre>winrt::IAsyncAction Example()\r\n{\r\n    \/\/ Create a dispatcher queue controller\r\n    \/\/ for a new dispatcher queue thread\r\n    auto controller =\r\n        winrt::DispatcherQueueController::CreateOnDedicatedThread();\r\n\r\n    \/\/ Switch to the dispatcher queue thread\r\n    co_await winrt::resume_foreground(controller.DispatcherQueue());\r\n\r\n    <span style=\"border: solid 1px currentcolor; border-bottom: none;\">\/\/ Get off the dispatcher queue thread                        <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">\/\/ before shutting it down.                                   <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">co_await winrt::resume_background();                          <\/span>\r\n    <span style=\"border: 1px currentcolor; border-style: none solid;\">\u00a0                                                             <\/span>\r\n    <span style=\"border: solid 1px currentcolor; border-top: none;\">\/\/ Shut down the dispatcher queue thread from a safe distance.<\/span>\r\n    co_await controller.ShutdownQueueAsync();\r\n\r\n    \/\/ This code is now safely running on a background thread.\r\n    co_await winrt::Launcher::LaunchUriAsync(\r\n        winrt::Uri{L\"https:\/\/microsoft.com\"});\r\n\r\n    DoSomeMoreStuff();\r\n}\r\n<\/pre>\n<p>\u00b9 PPL also has the default policy of resuming execution in the same COM apartment provided the task chain started with a Windows Runtime <code>IAsync\u00adXxx<\/code>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The dispatcher queue thread isn&#8217;t useful after it has shut down, so don&#8217;t try anything.<\/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-109438","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The dispatcher queue thread isn&#8217;t useful after it has shut down, so don&#8217;t try anything.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109438","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=109438"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109438\/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=109438"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109438"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109438"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}