{"id":105196,"date":"2021-05-07T07:00:00","date_gmt":"2021-05-07T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105196"},"modified":"2021-05-07T06:26:15","modified_gmt":"2021-05-07T13:26:15","slug":"20210507-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210507-00\/?p=105196","title":{"rendered":"A subtle way your await_suspend can access the coroutine frame when it shouldn&#8217;t"},"content":{"rendered":"<p>As we learned in the very start of the series on coroutines, <a title=\"C++ coroutines: Getting started with awaitable objects\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191209-00\/?p=103195\"> the <code>await_<wbr \/>suspend<\/code> method cannot access the coroutine frame once it arranges for the coroutine to resume<\/a> because that creates a race condition where the coroutine might already be resumed and possibly even run to completion before <code>await_<wbr \/>suspend<\/code> finishes.<\/p>\n<p>There&#8217;s a subtle way your <code>await_<wbr \/>suspend<\/code> can access the coroutine frame that doesn&#8217;t immediately look like it&#8217;s accessing the coroutine frame.<\/p>\n<p>That would be by throwing an exception.<\/p>\n<p>The coroutine transformation <a title=\"C++ coroutines: The initial and final suspend, and improving our return_value method\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210331-00\/?p=105028\"> puts the function body inside a giant <code>try<\/code>\/<code>catch<\/code> block<\/a>, and if any exception occurs, the coroutine regains control and hands the exception to the promise&#8217;s <code>unhandled_<wbr \/>exception<\/code> method.<\/p>\n<p>This includes the case where an exception is thrown from <code>await_<wbr \/>suspend<\/code>.<\/p>\n<p>If the exception is thrown before the <code>await_<wbr \/>suspend<\/code> arranges for the coroutine to be resumed, then everything works as expected: The coroutine catches the exception, saves it in the promise, and then goes to its <code>final_<wbr \/>suspend<\/code>. Note that the coroutine <i>has resumed<\/i>.<\/p>\n<p>If the exception is thrown after the <code>await_<wbr \/>suspend<\/code> arranges for the coroutine to be resumed, then you have a problem. Because now the coroutine is going to be resumed <i>twice<\/i>: once by the coroutine machinery that caught the exception and saved it in the promise, and again by whatever mechanism you used to arrange for the coroutine to be resumed.<\/p>\n<p>That&#8217;s not good.<\/p>\n<p>Furthermore, the <code>await_<wbr \/>suspend<\/code> is racing against the resumption, and if the resumption occurs first, then the coroutine might run all the way to completion and be destroyed, and now you&#8217;re trying to save the exception into an already-destructed promise.<\/p>\n<p>That&#8217;s also not good.<\/p>\n<p>So don&#8217;t do that.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hiding in the coroutine machinery.<\/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-105196","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Hiding in the coroutine machinery.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105196","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=105196"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105196\/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=105196"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105196"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105196"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}