{"id":103268,"date":"2019-12-26T07:00:00","date_gmt":"2019-12-26T15:00:00","guid":{"rendered":"http:\/\/devblogs.microsoft.com\/oldnewthing\/?p=103268"},"modified":"2019-12-25T22:01:18","modified_gmt":"2019-12-26T06:01:18","slug":"20191226-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191226-00\/?p=103268","title":{"rendered":"C++ coroutines: The problem of the DispatcherQueue task that runs too soon, part 4"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191225-00\/?p=103265\"> Last time<\/a>, we made another attempt to fix 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, and we found a nice place to put the synchronization object, namely in the awaiter, but even with that fix, we introduced additional memory barriers into the hot code path.<\/p>\n<p>But it turns out all this work was unnecessary. We just had to look at the problem a different way.<\/p>\n<p>The purpose of storing the result of <code>Try\u00adEnqueue<\/code> into <code>m_queued<\/code> is so that <code>await_<\/code><code>resume<\/code> can report whether the lambda was queued or not. But we can infer that information another way: The fact that our lambda is running means that got got queued. Because if the lambda were not queued, then it would never have run in the first place.<\/p>\n<p>This allows us to simplify the awaiter by making the lambda responsible for reporting that it was queued.<\/p>\n<pre>    bool await_suspend(coroutine_handle&lt;&gt; handle)\r\n    {\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">m_queued =<\/span><\/span>\r\n      <span style=\"color: blue;\">return<\/span>\r\n        m_dispatcher.TryEnqueue([<span style=\"color: blue;\">this,<\/span> handle]\r\n        {\r\n          <span style=\"color: blue;\">m_queued = true;<\/span>\r\n          handle();\r\n        });\r\n      <span style=\"color: red;\">\/\/ <span style=\"text-decoration: line-through;\">return m_queued;<\/span><\/span>\r\n    }\r\n<\/pre>\n<p>There are two cases to consider:<\/p>\n<p>First, the <code>Try\u00adEnqueue<\/code> could fail. In that case, <code>await_<\/code><code>suspend<\/code> returns <code>false<\/code>, and <code>m_queued<\/code> continues to have its original value (which is also <code>false<\/code>). The coroutine resumes immediately on the same thread, and the <code>await_<\/code><code>ready<\/code> will return <code>m_queued<\/code>, which is <code>false<\/code>. The value of <code>m_queued<\/code> correctly reports that the lambda was not queued.<\/p>\n<p>Otherwise, <code>Try\u00adEnqueue<\/code> succeeded, and this is the more interesting case. Since <code>await_<\/code><code>suspend<\/code> does not access any member variables after calling <code>Try\u00adEnqueue<\/code>, it doesn&#8217;t matter whether the lambda runs before or after <code>await_<\/code><code>suspend<\/code> returns.<\/p>\n<p>The <code>await_<\/code><code>suspend<\/code> returns <code>true<\/code> because the lambda was queued, and this permits the suspension of the coroutine to proceed. Nobody has updated <code>m_queued<\/code>, so it still has its initial value of <code>false<\/code>. This is an incorrect state of affairs, but that&#8217;s okay: We&#8217;ll fix it before anybody notices.<\/p>\n<p>When the lambda runs, it sets <code>m_queued<\/code> to <code>true<\/code>. This restores balance to the universe by bringing the <code>m_queued<\/code> member variable to a value consistent with what actually happened. Only after repairing <code>m_queued<\/code> do we invoke the <code>handle<\/code>. The two operations (updating <code>m_queued<\/code> and invoking the <code>handle<\/code>), so we don&#8217;t have a race condition between the setting of <code>m_queued<\/code> and its observation in <code>await_<\/code><code>ready<\/code>.<\/p>\n<p>You could say that we lazy-updated the <code>m_queued<\/code> member variable. It&#8217;s not safe to update it in <code>await_<\/code><code>suspend<\/code>, so we wait until the lambda. We didn&#8217;t have to pass the value of <code>true<\/code> explicitly to the lambda, because the lambda knows that <code>true<\/code> is the only value it could possibly be if the lambda is running.<\/p>\n<p>That wraps up our introduction to C++ coroutines. I haven&#8217;t even gotten a chance to get into promises and the other infrastructure needed to <i>create<\/i> coroutines.\u00b9 So far, we&#8217;ve just been looking at the infrastructure needed to create awaitable objects. Someday, I&#8217;ll write about promises, but I&#8217;m going to take a break for a bit.<\/p>\n<p><b>Bonus chatter<\/b>: Notice how my initial instinct for fixing this problem was writing fifty-some-odd lines of code. But stopping to think let me shrink it to about half that. And then stepping back and looking at the bigger issue allowed me to fix the problem by making small changes to two lines of code.<\/p>\n<p>\u00b9 This means that we will have to wait before we learn about the mysterious step 1 in the search for an awaiter.<\/p>\n<p>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>You can update it a different way.<\/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-103268","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>You can update it a different way.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103268","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=103268"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/103268\/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=103268"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=103268"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=103268"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}