Date: Wed, 12 Apr 2023 21:27:24 +0300
On 4/12/23 18:53, Edward Catmur wrote:
> On Mon, 10 Apr 2023, 08:57 Andrey Semashev via Std-Discussion,
> <std-discussion_at_[hidden]
> <mailto:std-discussion_at_[hidden]>> wrote:
>
> I don't think that updating a pointer during a context switch would be a
> deal breaker. The co_unhandled_exceptions() mechanism only needs to know
> the current coroutine state of the current thread, it doesn't need a
> list. If a linked list is needed to be able to return from one coroutine
> to another then such a list must be already in place.
>
> Now that I think of it, co_unhandled_exceptions() might not require any
> new pointers. The coroutine already has to keep a pointer to its state
> somewhere to be able to reference its local variables. I don't see why
> co_unhandled_exceptions() couldn't use that pointer.
>
> But how can co_unhandled_exceptions() *find* that pointer? The current
> coroutine state is not stored in per thread memory; it's solely on the
> stack. (The linked list that's "already in place" is the stack itself.)
The compiler could pass this pointer as a hidden parameter to
co_unhandled_exceptions() based on implementation-specific attribute, or
co_unhandled_exceptions() could be a compiler intrinsic to begin with.
Or, if the pointer is stored in a known fixed location of the parent
stack frame, co_unhandled_exceptions() could extract it from there. All
this is to say this task doesn't seem impossible.
> That said, you might be able to find it when unwinding through a
> coroutine stack frame. I'm still thinking about it, but it seems like it
> should work. In that case you've only got overhead added to coroutine
> creation, coroutine footprint, unwinding through a coroutine, and you've
> doubled the stack footprint and memory access of the success/failure
> scope guards, compared to a C++17 implementation using
> unhandled_exceptions only.
Not sure what you mean by doubling the stack footprint and memory access
of the scope guards. The size of the scope guards didn't change.
> But doesn't adding the information for co_unhandled_exceptions() to
> coroutine state break abi?
It should be implementable without breaking the ABI, if the coroutine
state is allocated and initialized by the runtime (and I assume it
should be the case). For example, you could put the added counter before
the data used by the coroutine (parameters and locals), at a negative
offset known to the runtime.
> When I say "the destruction of the scope guard is caused by a stack
> unwinding procedure" I mean literally that the stack unwinding procedure
> is present higher up in the call stack from the scope guard destructor.
> I'm choosing this criteria because that's what makes most sense to me in
> relation to the expected behavior of the scope guards.
>
> That doesn't work. Having an unwind caused destructor above in the stack
> from a scope guard destructor does not always mean that the scope guard
> is in a failure state. It feels like we're going round in circles here.
A scope guard destructor would conceal this information by setting the
uncaught exceptions counter to zero, as described below.
> > In a
> > perfect world, I would prefer to have scope guard-like
> functionality in
> > the core language (but not in the form of try/finally from Java).
> >
> > Even if we had scope guard in the core language, its actions would be
> > executed during stack unwinding, no?
>
> Yes, but a core language feature could have better syntax and better
> defined semantics as it wouldn't have to rely on unhandled_eceptions().
>
> As could a library feature with compiler support, and that wouldn't
> require new syntax.
A new syntax could offer more benefits, like not having to name the
scope guard, for example. It would also make the discussion re.
unhandled_exceptions() moot as it would simply not use it internally.
> On Mon, 10 Apr 2023, 08:57 Andrey Semashev via Std-Discussion,
> <std-discussion_at_[hidden]
> <mailto:std-discussion_at_[hidden]>> wrote:
>
> I don't think that updating a pointer during a context switch would be a
> deal breaker. The co_unhandled_exceptions() mechanism only needs to know
> the current coroutine state of the current thread, it doesn't need a
> list. If a linked list is needed to be able to return from one coroutine
> to another then such a list must be already in place.
>
> Now that I think of it, co_unhandled_exceptions() might not require any
> new pointers. The coroutine already has to keep a pointer to its state
> somewhere to be able to reference its local variables. I don't see why
> co_unhandled_exceptions() couldn't use that pointer.
>
> But how can co_unhandled_exceptions() *find* that pointer? The current
> coroutine state is not stored in per thread memory; it's solely on the
> stack. (The linked list that's "already in place" is the stack itself.)
The compiler could pass this pointer as a hidden parameter to
co_unhandled_exceptions() based on implementation-specific attribute, or
co_unhandled_exceptions() could be a compiler intrinsic to begin with.
Or, if the pointer is stored in a known fixed location of the parent
stack frame, co_unhandled_exceptions() could extract it from there. All
this is to say this task doesn't seem impossible.
> That said, you might be able to find it when unwinding through a
> coroutine stack frame. I'm still thinking about it, but it seems like it
> should work. In that case you've only got overhead added to coroutine
> creation, coroutine footprint, unwinding through a coroutine, and you've
> doubled the stack footprint and memory access of the success/failure
> scope guards, compared to a C++17 implementation using
> unhandled_exceptions only.
Not sure what you mean by doubling the stack footprint and memory access
of the scope guards. The size of the scope guards didn't change.
> But doesn't adding the information for co_unhandled_exceptions() to
> coroutine state break abi?
It should be implementable without breaking the ABI, if the coroutine
state is allocated and initialized by the runtime (and I assume it
should be the case). For example, you could put the added counter before
the data used by the coroutine (parameters and locals), at a negative
offset known to the runtime.
> When I say "the destruction of the scope guard is caused by a stack
> unwinding procedure" I mean literally that the stack unwinding procedure
> is present higher up in the call stack from the scope guard destructor.
> I'm choosing this criteria because that's what makes most sense to me in
> relation to the expected behavior of the scope guards.
>
> That doesn't work. Having an unwind caused destructor above in the stack
> from a scope guard destructor does not always mean that the scope guard
> is in a failure state. It feels like we're going round in circles here.
A scope guard destructor would conceal this information by setting the
uncaught exceptions counter to zero, as described below.
> > In a
> > perfect world, I would prefer to have scope guard-like
> functionality in
> > the core language (but not in the form of try/finally from Java).
> >
> > Even if we had scope guard in the core language, its actions would be
> > executed during stack unwinding, no?
>
> Yes, but a core language feature could have better syntax and better
> defined semantics as it wouldn't have to rely on unhandled_eceptions().
>
> As could a library feature with compiler support, and that wouldn't
> require new syntax.
A new syntax could offer more benefits, like not having to name the
scope guard, for example. It would also make the discussion re.
unhandled_exceptions() moot as it would simply not use it internally.
Received on 2023-04-12 18:27:39