{"id":103265,"date":"2019-12-25T07:00:00","date_gmt":"2019-12-25T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103265"},"modified":"2019-12-24T20:58:02","modified_gmt":"2019-12-25T04:58:02","slug":"20191225-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191225-00\/?p=103265","title":{"rendered":"C++ coroutines: The problem of the DispatcherQueue task that runs too soon, part 3"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191224-00\/?p=103262\"> Last time<\/a>, we fixed a race condition in C++\/WinRT&#8217;s <code>resume_<code><\/code>foreground(<code><\/code>Dispatcher\u00adQueue)<\/code> function when it tries to resume execution on a dispatcher queue. We did this by having the queued task wait until <code>await_<\/code><code>suspend<\/code> was finished before allowing the coroutine to resume. The hard part was finding a place to put the synchronization object, and we ended up putting it in the queued task&#8217;s lambda.<\/p>\n<p>But it turns out there&#8217;s another place we can put it, and it was in front of us the whole time.<\/p>\n<p>We can put it in the awaiter.<\/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    <span style=\"color: blue;\">slim_event ready;<\/span>\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      bool result = m_dispatcher.TryEnqueue([<span style=\"color: blue;\">this,<\/span> handle]\r\n        {\r\n          <span style=\"color: blue;\">ready.wait();<\/span>\r\n          handle();\r\n        });\r\n      m_queued = result;\r\n      <span style=\"color: blue;\">ready.signal();<\/span>\r\n      return result;\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>The awaiter is destructed when the coroutine resumes, and the coroutine resumes either when the coroutine handle is invoked (which we do in the lambda), or when the <code>await_<\/code><code>ready<\/code> or <code>await_<\/code><code>suspend<\/code> methods indicate that the resumption should proceed immediately.<\/p>\n<p>Therefore, members of the awaiter are good as long as we haven&#8217;t done any of those things.<\/p>\n<p>In the case of a successful <code>Try\u00adEnqueue<\/code>, the <code>await_<\/code><code>suspend<\/code> is not going to ask for immediate resumption. The resumption will occur when the lambda invokes the handler. But our lambda waits for <code>async_<\/code><code>suspend<\/code> to signal the event before invoking the handler, so the awaiter is still valid at that point.<\/p>\n<p>In the case of a failed <code>Try\u00adEnqueue<\/code>, the lambda will never run. Instead, the coroutine resumes after <code>await_<\/code><code>suspend<\/code> returns with a value of <code>false<\/code>. Prior to the return, the awaiter is still valid, so we can signal the event. (Mind you, there&#8217;s nobody listening for the signal.)<\/p>\n<p>This is a lot simpler than trying to dig the event out of the lambda.<\/p>\n<p>However, there is still quite a bit of overhead to this plan: While it&#8217;s true that the <code>slim_<\/code><code>event<\/code> runs entirely in user mode in the non-blocking case, it still generates a good number of memory barriers. The <code>slim_<\/code><code>event::<\/code><code>signal<\/code> method uses a memory barrier in order to ensure that <code>m_queued<\/code> is made visible to all threads before setting <code>signaled<\/code> to true. If it didn&#8217;t do this, then the <code>wait<\/code> function could see that <code>signaled<\/code> is true and run ahead with the wrong value of <code>m_queued<\/code>.<\/p>\n<p>Furthermore, after the <code>signal<\/code> method updates the <code>signaled<\/code> member, it calls <code>Wake\u00adBy\u00adAddress\u00adAll<\/code>, which will itself erect another memory barrier to make sure it found all the waiters (even if there are none).<\/p>\n<p>The memory barriers are necessary to ensure that the updated value of <code>m_queued<\/code> that was written by the <code>await_<\/code><code>suspend<\/code> can be ready by <code>await_<\/code><code>ready<\/code> in the case that the coroutine continues on the dispatcher thread.<\/p>\n<p>Next time, we&#8217;ll get rid of the memory barriers.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>The storage was right in front of your nose.<\/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-103265","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>The storage was right in front of your nose.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103265","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=103265"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103265\/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=103265"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103265"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103265"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}