{"id":103255,"date":"2019-12-23T07:00:00","date_gmt":"2019-12-23T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103255"},"modified":"2019-12-22T08:00:54","modified_gmt":"2019-12-22T16:00:54","slug":"20191223-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191223-00\/?p=103255","title":{"rendered":"C++ coroutines: The problem of the DispatcherQueue task that runs too soon, part 1"},"content":{"rendered":"<p>I was experiencing occasional crashes in C++\/WinRT&#8217;s <code>resume_foreground<\/code> function when it tries to resume execution on a dispatcher queue. Here&#8217;s a simplified version of that function:<\/p>\n<pre>auto resume_foreground(DispatcherQueue const&amp; dispatcher)\r\n{\r\n  struct awaitable\r\n  {\r\n    DispatcherQueue m_dispatcher;\r\n    bool m_queued = false;\r\n\r\n    bool await_ready()\r\n    {\r\n      return false;\r\n    }\r\n\r\n    bool await_suspend(coroutine_handle&lt;&gt; handle)\r\n    {\r\n      m_queued = m_dispatcher.TryEnqueue([handle]\r\n        {\r\n          handle();\r\n        });\r\n      return m_queued;\r\n    }\r\n\r\n    bool await_resume()\r\n    {\r\n      return m_queued;\r\n    }\r\n  };\r\n  return awaitable{ dispatcher };\r\n}\r\n<\/pre>\n<p>All you need to know about the <code>Dispatcher\u00adQueue<\/code> object is that the <code>Try\u00adEnqueue<\/code> method takes a delegate and schedules it to run on the dispatcher queue&#8217;s thread. If it is unable to do so (say, because the thread has already exited), then the function returns <code>false<\/code>. The return value of the <code>Try\u00adEnqueue<\/code> method is the result of the <code>co_await<\/code>.<\/p>\n<p>Let&#8217;s walk through how this function is intended to work.<\/p>\n<p>The <code>resume_<\/code><code>foreground<\/code> method returns an object that acts as its own awaiter. When a <code>co_await<\/code> occurs, the coroutine first calls <code>await_ready<\/code>, which returns <code>false<\/code>, meaning &#8220;Go ahead and suspend me.&#8221;<\/p>\n<p>Next, the coroutine calls <code>await_suspend<\/code>. This method tries to queue the resumption of the coroutine onto the dispatcher thread and remembers whether it succeeded in the <code>m_queued<\/code> member variable.<\/p>\n<p>Returning the value of <code>m_queued<\/code> means that if the continuation was successfully scheduled (<code>true<\/code>), the coroutine remains suspended until it is resumed when the handle is invoked. On the other hand, if the continuation was not successfully scheduled (<code>false<\/code>), then the suspension is abandoned, and execution resumes immediately on the same thread.<\/p>\n<p>Either way, when the coroutine resumes, it is told whether the rescheduling onto the dispatcher thread succeeded.<\/p>\n<p>Okay, now that you see how it is intended to work, can you spot the defect?<\/p>\n<p>This code violates one of the rules we gave when we were <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191209-00\/?p=103195\"> getting started with awaitable objects<\/a>: Once you arrange for the <code>handle<\/code> to be called, you cannot access any member variables because the coroutine may have resumed before <code>async_suspend<\/code> finishes.<\/p>\n<p>And that&#8217;s what&#8217;s happening here: The dispatcher queue is running the lambda even before the <code>async_suspend<\/code> can save the answer into <code>m_queued<\/code>. As a result, the code crashes (if you&#8217;re lucky) or corrupts memory (if you&#8217;re not).<\/p>\n<p>So we need to make sure the lambda doesn&#8217;t race ahead of <code>async_<code><\/code>suspend<\/code>.<\/p>\n<p>Next time, we&#8217;ll make our first attempt to fix this.<\/p>\n<p>(The fact that I call it our <i>first<\/i> attempt gives you a clue that it may take more than one try.)<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Variables disappearing out from under you.<\/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-103255","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Variables disappearing out from under you.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103255","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=103255"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103255\/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=103255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}