Doc. no. | P1356R0 |
Revises | none |
Date: | 2018-11-08 |
Project: | Programming Language C++ |
Reference: | ISO/IEC TS 22277, C++ Extensions for Coroutines |
Audience: | WG21 |
Reply to: | Gor Nishanov <gorn@microsoft.com> |
All proposed resolutions wording is relative to N4775.
Section: 11.4.4 [dcl.fct.def.coroutine] Status: Ready Submitter: Eric Niebler Opened: 2018-03-10 Last modified: 2018-05-05
[EWG Approved Rapperswil-2018/06/09]
[CWG Approved San Diego-2018/11/08]
Issue:
Currently, an unhandled exception can never escape the user-authored body of the coroutine with triggering undefined behavior.
{ P p; co_await p.initial_suspend(); // initial suspend point try { F // user authored body } catch(...) { p.unhandled_exception(); } final_suspend: co_await p.final_suspend(); // final suspend point }
An exception from F
is captured by the try-catch and
a customization point unhandled_exception
is called,
where, typically, an exception_ptr is created and propagated to
the consumer awaiting on async task, or, in case of a generator,
will be delivered to the user
when they dereference the iterator.
Though the current behavior is perfectly reasonable for asynchronous
scenarios, it is sub-optimal
for synchronous generators. Capturing an exception, storing it in an
exception_ptr
and then rethrowing the exception during,
say, iterator's operator*
is a needless work if the desired
behavior is to let the exception propagate to the caller
whenever it asks for the next value.
Background information:
When a coroutine is first invoked, any exception thrown before entering
the user-authored body (for example allocation failure, promise constructor failure,
failure to copy parameters, etc) propagates into the caller as with any
normal function call. However, when the coroutine suspends and subsequently resumed,
if an exception is thrown by an evaluation of
p.unhandled_exception()
or
an evaluation of co_await p.final_suspend()
the behavior is undefined. Note that a coroutine can be only resumed or destroyed
when suspended at a particular suspend point. An exception leaving the coroutine
at arbitrary point of the execution leaves the coroutine in the undefined state.
The proposed resolution is to eliminate the undefined behavior in the following manner:
p.unhandled_exception()
and, in that case, consider the coroutine to be at the final suspend point. Reminder: when
a coroutine is at the final suspend point, the coroutine can only be
destroyed and a call to member function done()
of the
coroutine handle associated with that coroutine returns true
.
co_await p.final_suspend()
by stating that final_suspend
member function of
the coroutine promise and await_resume
, await_ready
,
and await_suspend
members of the object returned from
final_suspend
shall have non-throwing exception specification.
This resolution allows generator implementations to define unhandled_exception
as follows:
void unhandled_exception() { throw; }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.
Proposed Wording:
In subclause 11.4.4, add two new paragraph after paragraph 11.
p.unhandled_exception()
exits via an exception, the coroutine is considered suspended at the final suspend point.
co_await p.final_suspend()
shall not be potentially-throwing ([except.spec]).
static
and thread_local
local variables ill-formedSection: 8.3.8/2 [expr.await] Status: Ready Submitter: Richard Smith Opened: 2018-03-25 Last modified: 2018-03-25
[EWG Approved Rapperswil-2018/06/09]
[CWG Approved San Diego-2018/11/08]
Proposed wording:
Add underlined text to 8.3.8/2:An await-expression shall appear only in a potentially-evaluated expression within the compound-statement of a function-body outside of a handler (Clause 18). In a declaration-statement or in the simple-declaration (if any) of a for-init-statement, anawait-expressionawait-expression shall appear only in an initializer of that declaration-statement or simple-declaration. An await-expression shall not appear in the initializer of a block-scope variable with static or thread storage duration.