{"id":111400,"date":"2025-07-22T07:00:00","date_gmt":"2025-07-22T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111400"},"modified":"2025-07-22T11:33:06","modified_gmt":"2025-07-22T18:33:06","slug":"20250722-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250722-00\/?p=111400","title":{"rendered":"Being more adamant about reporting that C++\/WinRT was unable to resume execution on a dispatcher thread"},"content":{"rendered":"<p>Last time, <a title=\"What happens if C++\/WinRT is unable to resume execution on a dispatcher thread?\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250721-00\/?p=111396\"> we saw what happens if C++\/WinRT is unable to resume execution on a dispatcher thread<\/a>: If you use <code>winrt::<wbr \/>resume_<wbr \/>foreground<\/code> with a <code>Core\u00adDispatcher<\/code>, then the coroutine simply never resumes (which means that it appears to have hung) and leaks. If you use <code>winrt::<wbr \/>resume_<wbr \/>foreground<\/code> with a <code>Dispatcher\u00adQueue<\/code>, then the result of the <code>co_await<\/code> tells you whether the thread switch was successful, but in practice nobody actually checks the result.<\/p>\n<p>Both of these are just bad situations to be in. In the <code>Dispatcher\u00adQueue<\/code> case, you have to remember to check a value that in practice nobody ever checks. In the <code>Core\u00adDispatcher<\/code> case, there&#8217;s simply nothing you can do at all.<\/p>\n<p>The Windows Implementation Library provides an alternative.<\/p>\n<p>We get rid of the <a href=\"https:\/\/blog.codinghorror.com\/falling-into-the-pit-of-success\/\"> pit of failure<\/a>. None of this &#8220;check a value that is easy to overlook&#8221;. If the coroutine cannot resume on the dispatcher thread, it <a href=\"https:\/\/github.com\/microsoft\/wil\/blob\/158863d89f6b462c60fb56b65ae4db6afee98adf\/include\/wil\/cppwinrt_helpers.h#L155\"> throws an exception<\/a>, specifically <code>HRESULT_<wbr \/>FROM_<wbr \/>WIN32(<wbr \/>ERROR_<wbr \/>NO_<wbr \/>TASK_<wbr \/>QUEUE)<\/code>. It&#8217;s hard to ignore an exception. You need to write special &#8220;exception-ignoring&#8221; code. And as a rule, C++\/WinRT uses exceptions to report that things have gone wrong, so this is consistent with the rest of the C++\/WinRT library.<\/p>\n<p>Now we can complete the table of behaviors if you call <code>resume_<wbr \/>foreground<\/code> and the dispatcher is unable to accept the work item.<\/p>\n<table class=\"cp3\" style=\"border-collapse: collapse;\" border=\"1\" cellspacing=\"0\" cellpadding=\"3\">\n<tbody>\n<tr>\n<th>\u00a0<\/th>\n<th>winrt::<wbr \/>resume_<wbr \/>foreground<\/th>\n<th>wil::<wbr \/>resume_<wbr \/>foreground<\/th>\n<\/tr>\n<tr>\n<th>CoreDispatcher<\/th>\n<td>hang<\/td>\n<td>throw &#8220;no task queue&#8221;<\/td>\n<\/tr>\n<tr>\n<th>DispatcherQueue<\/th>\n<td>return false<\/td>\n<td>throw &#8220;no task queue&#8221;<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>One thing to note is that the &#8220;no task queue&#8221; exception is thrown from an arbitrary thread. The original thread is no longer available, since we suspended on it, and the thread is now doing something else. And the destination thread is not available because that&#8217;s why we&#8217;re throwing the exception. We&#8217;re stuck in a no-man&#8217;s land where nobody has access to a thread, so the exception is just thrown from wherever it can. If you need to run destructors on or handle the exception from a specific thread, you&#8217;ll have to switch to that thread yourself.<\/p>\n<p><b>Bonus chatter<\/b>: How does <code>wil::<wbr \/>resume_<wbr \/>foreground()<\/code> work?<\/p>\n<p>The client-provided delegate is wrapped inside another delegate which keeps track of whether it has ever been called. If the delegate finds itself destructed without being called,\u00b9 then it knows that the thread switch failed, and it sets <code>orphaned<\/code> to <code>true<\/code> before manually resuming the coroutine. The awaiter&#8217;s <code>await_<wbr \/>resume()<\/code> function does the work of throwing the &#8220;no task queue&#8221; exception if the <code>orphaned<\/code> flag is set.<\/p>\n<p>\u00b9 There&#8217;s an edge case here: If the dispatcher rejects the delegate with an exception, then propagate that exception and don&#8217;t throw another exception for an uncalled delegate.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Better versions in a different box.<\/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-111400","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Better versions in a different box.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111400","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=111400"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111400\/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=111400"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111400"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111400"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}