{"id":109434,"date":"2024-02-22T07:00:11","date_gmt":"2024-02-22T15:00:11","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=109434"},"modified":"2024-02-22T09:04:22","modified_gmt":"2024-02-22T17:04:22","slug":"gotcha-dont-forget-to-shut-down-your-dispatcher-queues","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20240222-11\/?p=109434","title":{"rendered":"Gotcha: Don&#8217;t forget to shut down your dispatcher queues"},"content":{"rendered":"<p>If you need a <code>Dispatcher\u00adQueue<\/code>, you can create a <code>Dispatcher\u00adQueue\u00adController<\/code> and then read the <code>Dispatcher\u00adQueue<\/code> property to obtain the associated <code>Dispatcher\u00adQueue<\/code>.<\/p>\n<p>But don&#8217;t throw away that <code>Dispatcher\u00adQueue\u00adController<\/code>!<\/p>\n<pre>\/\/ C#\r\nDispatcherQueueController controller =\r\n    DispatcherQueueController.CreateOnDedicatedThread();\r\nDispatcherQueue queue = controller.DispatcherQueue;\r\n\r\n\/\/ C++\/WinRT\r\nDispatcherQueueController controller =\r\n    DispatcherQueueController::CreateOnDedicatedThread();\r\nDispatcherQueue queue = controller.DispatcherQueue();\r\n\r\n\/\/ C++\/CX\r\nDispatcherQueueController^ controller =\r\n    DispatcherQueueController::CreateOnDedicatedThread();\r\nDispatcherQueue^ queue = controller-&gt;DispatcherQueue;\r\n\r\n\/\/ C++\/WRL\r\nComPtr&lt;IDispatcherQueueControllerStatics&gt; statics;\r\nTHROW_IF_FAILED(\r\n    GetActivationFactory(HStringReference(\r\n    RuntimeClass_Windows_System_DispatcherQueueController).Get(),\r\n    &amp;statics));\r\n\r\nComPtr&lt;IDispatcherQueueController&gt; controller;\r\nTHROW_IF_FAILED(\r\n    statics-&gt;CreateOnDedicatedThread(&amp;controller));\r\n\r\nComPtr&lt;IDispatcherQueue&gt; queue;\r\nTHROW_IF_FAILED(\r\n    controller-&gt;get_DispatcherQueue(&amp;queue));\r\n<\/pre>\n<p>The <code>Dispatcher\u00adQueue<\/code> runs until it is shut down by a call to <code>Dispatcher\u00adQueue\u00adController.<wbr \/>Shutdown\u00adQueue\u00adAsync<\/code>. If you throw away the <code>Dispatcher\u00adQueue\u00adController<\/code>, then you have no way of shutting down the <code>Dispatcher\u00adQueue<\/code>, and it will run forever, long after the destruction of any objects which posted work to the <code>Dispatcher\u00adQueue<\/code>. It just sits there patiently waiting for new work which will never arrive, until it finally gets the rug pulled out from under it at process termination.<\/p>\n<p>If you create a dispatcher queue each time some action occurs, make sure to shut down that dispatcher queue when you are finished. Otherwise, you&#8217;re going to leak dispatcher queues and eventually run out of window manager resources.<\/p>\n<pre>\/\/ C#\r\n\r\nclass MyThing\r\n{\r\n    public MyThing()\r\n    {\r\n        \/\/ Oops\r\n        m_queue =\r\n            DispatcherQueueController.\r\n            CreateOnDedicatedThread().\r\n            DispatcherQueue;\r\n    }\r\n\r\n    DispatcherQueue m_queue;\r\n}\r\n\r\n\/\/ C++\/WinRT\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        \/\/ Oops\r\n        m_queue =\r\n            DispatcherQueueController::\r\n            CreateOnDedicatedThread().\r\n            DispatcherQueue();\r\n    }\r\n\r\nprivate:\r\n    DispatcherQueue m_queue{ nullptr };\r\n};\r\n\r\n\/\/ C++\/CX\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        \/\/ Oops\r\n        m_queue =\r\n            DispatcherQueueController::\r\n            CreateOnDedicatedThread()-&gt;\r\n            DispatcherQueue;\r\n    }\r\n\r\nprivate:\r\n    DispatcherQueue^ m_queue;\r\n};\r\n\r\n\/\/ C++\/WRL\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        \/\/ Oops\r\n        ComPtr&lt;IDispatcherQueueControllerStatics&gt; statics;\r\n        THROW_IF_FAILED(\r\n            GetActivationFactory(HStringReference(\r\n            RuntimeClass_Windows_System_DispatcherQueueController).Get(),\r\n            &amp;statics));\r\n\r\n        ComPtr&lt;IDispatcherQueueController&gt; controller;\r\n        THROW_IF_FAILED(\r\n            statics-&gt;CreateOnDedicatedThread(&amp;controller));\r\n\r\n        THROW_IF_FAILED(\r\n            controller-&gt;get_DispatcherQueue(&amp;m_queue));\r\n    }\r\n\r\nprivate:\r\n    ComPtr&lt;IDispatcherQueue&gt; m_queue;\r\n};\r\n<\/pre>\n<p>In the above examples, we create a dispatcher queue and throw away the controller, leaving us no way to shut down the queue when the <code>MyThing<\/code> destructs.<\/p>\n<p>You have to save the <code>Dispatcher\u00adQueue\u00adController<\/code> so that you can call <code>Shutdown\u00adQueue\u00adAsync<\/code>\u00b9 to clean up the dispatcher queue.<\/p>\n<p>One way to do this is to have an explicit <code>Shut\u00adDown\u00adAsync<\/code> method that must be called prior to destruction.<\/p>\n<pre>\/\/ C#\r\n\r\nclass MyThing\r\n{\r\n    public MyThing()\r\n    {\r\n        m_controller =\r\n            DispatcherQueueController.\r\n            CreateOnDedicatedThread();\r\n        m_queue = m_controller. DispatcherQueue;\r\n    }\r\n\r\n    public Task ShutDownAsync()\r\n    {\r\n        return m_controller.ShutdownQueueAsync();\r\n    }\r\n\r\n    DispatcherQueueController m_controller;\r\n    DispatcherQueue m_queue;\r\n}\r\n\r\n\/\/ C++\/WinRT\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        m_controller =\r\n            DispatcherQueueController::\r\n            CreateOnDedicatedThread();\r\n        m_queue = m_controller.DispatcherQueue();\r\n    }\r\n\r\n    IAsyncAction ShutDownAsync()\r\n    {\r\n        return m_controller.ShutdownQueueAsync();\r\n    }\r\n\r\nprivate:\r\n    DispatcherQueueController m_controller{ nullptr };\r\n    DispatcherQueue m_queue{ nullptr };\r\n};\r\n\r\n\/\/ C++\/CX\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        m_controller =\r\n            DispatcherQueueController::\r\n            CreateOnDedicatedThread();\r\n        m_queue = m_controller-&gt;DispatcherQueue;\r\n    }\r\n\r\n    IAsyncAction ShutDownAsync()\r\n    {\r\n        return m_controller-&gt;ShutdownQueueAsync();\r\n    }\r\n\r\nprivate:\r\n    DispatcherQueueController^ m_controller;\r\n    DispatcherQueue^ m_queue;\r\n};\r\n\r\n\/\/ C++\/WRL\r\n\r\nclass MyThing\r\n{\r\npublic:\r\n    MyThing()\r\n    {\r\n        ComPtr&lt;IDispatcherQueueControllerStatics&gt; statics;\r\n        THROW_IF_FAILED(\r\n            GetActivationFactory(HStringReference(\r\n            RuntimeClass_Windows_System_DispatcherQueueController).Get(),\r\n            &amp;statics));\r\n\r\n        THROW_IF_FAILED(\r\n            statics-&gt;CreateOnDedicatedThread(&amp;m_controller));\r\n\r\n        THROW_IF_FAILED(\r\n            controller-&gt;get_DispatcherQueue(&amp;m_queue));\r\n    }\r\n\r\n    HRESULT ShutDownAsync(IAsyncAction** operation)\r\n    {\r\n        return m_controller-&gt;ShutdownQueueAsync(operation);\r\n    }\r\n\r\nprivate:\r\n    ComPtr&lt;IDispatcherQueueController&gt; m_controller;\r\n    ComPtr&lt;IDispatcherQueue&gt; m_queue;\r\n};\r\n<\/pre>\n<p>This sounds great, but there&#8217;s a catch. We&#8217;ll look into it next time.<\/p>\n<p>\u00b9 The method name is spelled incorrectly. It should be <code>Shut\u00adDown\u00adQueue\u00adAsync<\/code>, because &#8220;shut down&#8221; (two words) is a verb, whereas &#8220;shutdown&#8221; (one word) is a noun.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Keep that dispatcher queue controller around, or you&#8217;ll never be able to clean up.<\/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-109434","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Keep that dispatcher queue controller around, or you&#8217;ll never be able to clean up.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109434","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=109434"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/109434\/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=109434"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=109434"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=109434"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}