<div dir="ltr">I don&#39;t think co_unhandled_exceptions adds anything does it? Either we automate storing the thread_local on yield and loading on resume or we don&#39;t. If we do, then the current thread_local query works fine.<div><br></div><div>If we had a way to *write* to unhandled exceptions we could do this in coroutine library code and make it QOI for a given coroutine implementation, and that doesn&#39;t have to be coroutine specific: std::set_uncaught_exceptions(cnt). We can already inject arbitrary state into the coroutine frame that&#39;s hidden from the coroutine author: in folly we maintain a stack trace for the debugger to walk, as one example.</div><div><br></div><div>All that said, uncaught_exceptions is a nasty piece of global state in the way of the optimizer, and this is a good example of more overhead that depending on it could create. It might be better to warn about using scope_fail across a yield point in the same way we need to be better at warning against use of locks or thread_locals in general. One big advantage that co_await/co_yield have over fibers is that we know where the yield points are and can make this logic work.</div><div><br></div><div>Lee</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 13 Apr 2023 at 06:04, Edward Catmur via Std-Discussion &lt;<a href="mailto:std-discussion@lists.isocpp.org">std-discussion@lists.isocpp.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="auto"><div><br><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Thu, 13 Apr 2023, 06:35 Andrey Semashev via Std-Discussion, &lt;<a href="mailto:std-discussion@lists.isocpp.org" target="_blank">std-discussion@lists.isocpp.org</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">On 4/13/23 05:16, Edward Catmur wrote:<br>
&gt; <br>
&gt; <br>
&gt; On Wed, 12 Apr 2023 at 15:27, Andrey Semashev via Std-Discussion<br>
&gt; &lt;<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a><br>
&gt; &lt;mailto:<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a>&gt;&gt; wrote:<br>
&gt; <br>
&gt;     On 4/12/23 18:53, Edward Catmur wrote:<br>
&gt;     &gt; On Mon, 10 Apr 2023, 08:57 Andrey Semashev via Std-Discussion,<br>
&gt;     &gt; &lt;<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a><br>
&gt;     &lt;mailto:<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a>&gt;<br>
&gt;     &gt; &lt;mailto:<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a><br>
&gt;     &lt;mailto:<a href="mailto:std-discussion@lists.isocpp.org" rel="noreferrer" target="_blank">std-discussion@lists.isocpp.org</a>&gt;&gt;&gt; wrote:<br>
&gt;     &gt;<br>
&gt;     &gt;     I don&#39;t think that updating a pointer during a context switch<br>
&gt;     would be a<br>
&gt;     &gt;     deal breaker. The co_unhandled_exceptions() mechanism only<br>
&gt;     needs to know<br>
&gt;     &gt;     the current coroutine state of the current thread, it doesn&#39;t<br>
&gt;     need a<br>
&gt;     &gt;     list. If a linked list is needed to be able to return from one<br>
&gt;     coroutine<br>
&gt;     &gt;     to another then such a list must be already in place.<br>
&gt;     &gt;<br>
&gt;     &gt;     Now that I think of it, co_unhandled_exceptions() might not<br>
&gt;     require any<br>
&gt;     &gt;     new pointers. The coroutine already has to keep a pointer to<br>
&gt;     its state<br>
&gt;     &gt;     somewhere to be able to reference its local variables. I don&#39;t<br>
&gt;     see why<br>
&gt;     &gt;     co_unhandled_exceptions() couldn&#39;t use that pointer.<br>
&gt;     &gt;<br>
&gt;     &gt; But how can co_unhandled_exceptions() *find* that pointer? The current<br>
&gt;     &gt; coroutine state is not stored in per thread memory; it&#39;s solely on the<br>
&gt;     &gt; stack. (The linked list that&#39;s &quot;already in place&quot; is the stack<br>
&gt;     itself.)<br>
&gt; <br>
&gt;     The compiler could pass this pointer as a hidden parameter to<br>
&gt;     co_unhandled_exceptions() based on implementation-specific attribute, or<br>
&gt;     co_unhandled_exceptions() could be a compiler intrinsic to begin with.<br>
&gt;     Or, if the pointer is stored in a known fixed location of the parent<br>
&gt;     stack frame, co_unhandled_exceptions() could extract it from there. All<br>
&gt;     this is to say this task doesn&#39;t seem impossible.<br>
&gt; <br>
&gt;     &gt; That said, you might be able to find it when unwinding through a<br>
&gt;     &gt; coroutine stack frame. I&#39;m still thinking about it, but it seems<br>
&gt;     like it<br>
&gt;     &gt; should work. In that case you&#39;ve only got overhead added to coroutine<br>
&gt;     &gt; creation, coroutine footprint, unwinding through a coroutine, and<br>
&gt;     you&#39;ve<br>
&gt;     &gt; doubled the stack footprint and memory access of the success/failure<br>
&gt;     &gt; scope guards, compared to a C++17 implementation using<br>
&gt;     &gt; unhandled_exceptions only. <br>
&gt; <br>
&gt;     Not sure what you mean by doubling the stack footprint and memory access<br>
&gt;     of the scope guards. The size of the scope guards didn&#39;t change.<br>
&gt; <br>
&gt; But *how* do scope_success and scope_failure access the current<br>
&gt; coroutine&#39;s co_uncaught_exceptions() counter? Obviously it&#39;s not a<br>
&gt; problem if they&#39;re complete automatic objects of the coroutine frame,<br>
&gt; but otherwise? e.g. if they&#39;re subobjects or dynamically allocated? I&#39;d<br>
&gt; think this would require stack walking (non-destructive unwinding),<br>
&gt; which is hugely expensive, or constructing a thread-local linked list of<br>
&gt; coroutines, which has continuous overhead.<br>
<br>
I think you are confusing the storage used for the scope guard object<br>
with the stack frame. co_uncaught_exceptions() doesn&#39;t need or use the<br>
scope guard object, it doesn&#39;t care where it is allocated or whether it<br>
exists at all. What co_uncaught_exceptions() *may* need is its caller&#39;s<br>
stack frame, if it is implemented in such a way that it uses the stack<br>
frame to obtain the pointer to the coroutine state (or to discover<br>
whether it is a coroutine at all).<br>
<br>
Now that I think of it more, perhaps using the caller&#39;s stack frame is<br>
not a viable idea after all, since the immediate caller of<br>
co_uncaught_exceptions() may not be a coroutine, but a normal function<br>
called within a coroutine. However, as I noted earlier, there are other<br>
possible implementations, including not involving TLS. But using TLS to<br>
store a pointer to the current coroutine state would be the simplest<br>
solution, of course.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Right. But that would require a linked list, since otherwise there&#39;s no way to restore the previous value when a coroutine suspends or returns. </div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">&gt; And what for scope_success and scope_failure that aren&#39;t constructed<br>
&gt; from within coroutines at all? How far do they look to determine to use<br>
&gt; uncaught_exceptions() instead?<br>
<br>
The scope guard would always use co_uncaught_exceptions(). When called<br>
not within a coroutine (meaning, there are no coroutines higher up the<br>
stack), co_uncaught_exceptions() would be equivalent to the<br>
uncaught_exceptions() we currently have.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">And how does co_uncaught_exceptions() know that there are no coroutines higher up the stack? It sounds like a thread local linked list is a necessity. </div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">&gt; And what if a dynamically allocated scope_success/scope_failure is<br>
&gt; constructed in one coroutine, but then has ownership transferred to<br>
&gt; another stack (which may or may not be a coroutine)?<br>
<br>
In that case, the cached number of uncaught exceptions may become not<br>
actual, depending on what kind of transfer you make. Yes, this may break<br>
scope_success/scope_fail, unfortunately. I&#39;d be willing to mark such use<br>
of scope_success/scope_fail UB, as this is arguably a case of the user<br>
explicitly doing something incompatible with those scope guards&#39; design,<br>
as opposed to using scope guards in the conventional way within coroutines.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Right, so this design is still fairly fragile. Which behaviors would you define, beyond the absolute minimum of scope guards as complete automatic objects? </div><div dir="auto"><br></div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">&gt;     &gt;     When I say &quot;the destruction of the scope guard is caused by a<br>
&gt;     stack<br>
&gt;     &gt;     unwinding procedure&quot; I mean literally that the stack unwinding<br>
&gt;     procedure<br>
&gt;     &gt;     is present higher up in the call stack from the scope guard<br>
&gt;     destructor.<br>
&gt;     &gt;     I&#39;m choosing this criteria because that&#39;s what makes most<br>
&gt;     sense to me in<br>
&gt;     &gt;     relation to the expected behavior of the scope guards.<br>
&gt;     &gt;<br>
&gt;     &gt; That doesn&#39;t work. Having an unwind caused destructor above in the<br>
&gt;     stack<br>
&gt;     &gt; from a scope guard destructor does not always mean that the scope<br>
&gt;     guard<br>
&gt;     &gt; is in a failure state. It feels like we&#39;re going round in circles<br>
&gt;     here. <br>
&gt; <br>
&gt;     A scope guard destructor would conceal this information by setting the<br>
&gt;     uncaught exceptions counter to zero, as described below.<br>
&gt; <br>
&gt; Is this a revision to your design, an extension, or an alternate design?<br>
&gt; I&#39;m having trouble keeping track.<br>
<br>
No, it&#39;s the same design. Or you could say it&#39;s an evolution of the same<br>
design during the discussion. It&#39;s all about co_uncaught_exceptions()<br>
and co_set_uncaught_exceptions(), the latter being used for concealing<br>
the pending exceptions from the scope guard action, as I have shown earlier.<br></blockquote></div></div><div dir="auto"><br></div><div dir="auto">Is this a necessary part of the design or can it be omitted? Which scenarios does it help with? </div><div dir="auto"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
</blockquote></div></div></div>
-- <br>
Std-Discussion mailing list<br>
<a href="mailto:Std-Discussion@lists.isocpp.org" target="_blank">Std-Discussion@lists.isocpp.org</a><br>
<a href="https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion" rel="noreferrer" target="_blank">https://lists.isocpp.org/mailman/listinfo.cgi/std-discussion</a><br>
</blockquote></div>

