{"id":105186,"date":"2021-05-05T07:00:00","date_gmt":"2021-05-05T14:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=105186"},"modified":"2021-05-05T06:53:32","modified_gmt":"2021-05-05T13:53:32","slug":"20210505-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210505-00\/?p=105186","title":{"rendered":"Why is coroutine_handle::resume() potentially-throwing?"},"content":{"rendered":"<p>In our <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20191209-00\/?p=103195\"> explorations of making <code>co_await<\/code>able objects<\/a>, we had largely been <a title=\"Creating a co_await awaitable signal that can be awaited multiple times, part 6\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20210308-00\/?p=104938\"> ignoring the possibility of the coroutine handle throwing an exception upon resume<\/a>. But according to the language specification, the <code>resume<\/code> method (and its equivalent, the <code>operator()<\/code> overloaded function call operator) is potentially-throwing. Is this an oversight or an intentional decision?<\/p>\n<p>Well, the <code>noop_<wbr \/>coroutine<\/code>&#8216;s coroutine handle does mark its <code>resume()<\/code> method as <code>noexcept<\/code>, so it&#8217;s not like the authors of the coroutine specification simply forgot about <code>noexcept<\/code>. They consciously put it on the resumption of a <code>noop_<wbr \/>coroutine<\/code>, but omitted it from other coroutines.<\/p>\n<p>What&#8217;s more, if you look at libraries that operate on coroutines, all of them treat the <code>resume<\/code> method as if it were <code>noexcept<\/code>.<\/p>\n<p>What&#8217;s the deal?<\/p>\n<p>Gor Nishanov explained it to me.<\/p>\n<p>Allowing <code>resume<\/code> to throw was introduced in <a href=\"http:\/\/www.open-std.org\/jtc1\/sc22\/wg21\/docs\/papers\/2018\/p0664r6.html#25\"> P0664R6 section 25<\/a>, with this remark:<\/p>\n<blockquote class=\"q\">\n<p>This resolution allows generator implementations to define unhandled_exception as follows:<\/p>\n<pre>  void unhandled_exception() { throw; } \r\n<\/pre>\n<p>With this implementation, if a user of the generator pulls the next value, and during computation of the next value an exception will occur in the user authored body it will be propagate back to the user and the coroutine will be put into a final suspend state and ready to be destroyed when generator destructors is run.<\/p>\n<\/blockquote>\n<p>Yeah but what does that all mean?<\/p>\n<p>The scenario here is the use of coroutines as generators.<\/p>\n<p>If a generator encounters an exception, the normal mechanism would be for the exception to be captured in the coroutine&#8217;s <code>unhandled_<wbr \/>exception<\/code> method so that it can be re-thrown when the caller performs an <code>await_resume<\/code>. But if the generator is synchronous (performs no <code>co_await<\/code> operations), then it is more efficient to just let the exception propagate across the coroutine boundary directly to the caller.<\/p>\n<p>The coroutine implementation (specifically, the promise) can indicate that it wants the exception to propagate by rethrowing the exception in <code>unhandled_<wbr \/>exception<\/code>, rather than capturing it.<\/p>\n<p>But if you&#8217;re not in the case of a synchronous generator (and when dealing with coroutines as tasks, you won&#8217;t be), then <code>resume<\/code> is indeed nonthrowing.<\/p>\n<p><b>Bonus reading<\/b>: Another reason for not marking <code>resume()<\/code> as <code>noexcept<\/code> is that <code>resume()<\/code> requires that the coroutine be suspended. The presence of a precondition means that, according to <a href=\"https:\/\/quuxplusone.github.io\/blog\/2018\/04\/25\/the-lakos-rule\/\"> the Lakos Rule<\/a>, the function should not be marked <code>noexcept<\/code>. This allows the implementation to choose to report the precondition violation in the form of an exception.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>It enables a very specific usage scenario.<\/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-105186","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>It enables a very specific usage scenario.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105186","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=105186"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/105186\/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=105186"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=105186"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=105186"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}