Doc. no. | P0913R1 |
Revises | P0913R0 |
Date: | 2018-03-15 |
Reference: | ISO/IEC TS 22277, C++ Extensions for Coroutines |
Audience: | EWG |
Reply to: | Gor Nishanov <gorn@microsoft.com> |
Currently Coroutines TS only supports asymmetric control transfer where suspend always returns control back to the current coroutine caller or resumer. In order to emulate symmetric coroutine to coroutine control transfer, one needs to build a queue and a scheduler: prior to suspension, a coroutine enqueues the next-to-resume coroutine into a scheduler queue and then returns control to its caller. At some point, control will transfer to a scheduler loop that dequeues and resumes coroutines.
Recursive generators, zero-overhead futures and other facilities require efficient coroutine to coroutine control transfer. Involving a queue and a scheduler makes coroutine to coroutine control transfer inefficient. Coroutines need direct and efficient way of expressing the desired behavior.
await_suspend
to designate a coroutine to perform a symmetric
control transfer to.
noop_coroutine
that returns a handle to a coroutine
that has no observable side effects when resumed. Having such a coroutine handle
allows library writer to perform either symmetric or asymmetric control transfer
based on runtime considerations.Before | After |
---|---|
thread_local queue<coroutine_handle<>> resume_queue; void scheduler_loop() { while (!resume_queue.empty()) { resume_queue.front().resume(); resume_queue.pop_front(); } } struct Awaiter { ... void await_suspend(coroutine_handle<> h) { ... if (cond) resume_queue.push(next_coro); } }; |
struct Awaiter { ... auto await_suspend(coroutine_handle<> h) { ... return cond ? next_coro : noop_coroutine(); } }; |
[All proposed wording is relative to N4723].
Modify paragraph 5.3.8/3 as follows
(3.7) —await-suspend is the expressione .await_suspend(
h )
, which shall be a prvalue of typevoid
,orbool
, orstd::experimental::coroutine_handle<Z>
for some typeZ
.
Modify paragraph 5.3.8/5 as follows:
5 The await-expression evaluates the await-ready expression, then:
(5.1) — If the result is
false
, the coroutine is considered suspended. Then, the await-suspend expression is evaluated. If that expression has typestd::experimental::coroutine_handle<Z>
and evaluates to a value s, the coroutine referred to by s is resumed as if by a call s.resume()
. [Note: Any number of coroutines may be successively resumed in this fashion, eventually returning control flow to the current coroutine caller or resumer (8.4.4) -- end note] If that expression has typebool
and evaluates tofalse
, the coroutine is resumed. If that expression exits via an exception, the exception is caught, the coroutine is resumed, and the exception is immediately re-thrown (15.1). Otherwise, control flow returns to the current coroutine caller or resumer (8.4.4) without exiting any scopes (6.6).
Add the following to <experimental/coroutine>
synopsis in [support.coroutine]/1:
namespace std { namespace experimental { inline namespace coroutines_v1 { // classnoop_coroutine_promise
18.11.4 struct noop_coroutine_promise; // 18.11.1 coroutine traits template <typename R, typename... ArgTypes> struct coroutine_traits; // 18.11.2 coroutine handle template <typename Promise = void> struct coroutine_handle; template <> struct coroutine_handle<noop_coroutine_promise>; // noop coroutine handle using noop_coroutine_handle = coroutine_handle<noop_coroutine_promise>; // noop coroutine 18.11.5 noop_coroutine_handle noop_coroutine() noexcept; ...
Add the following to coroutine_handle
synopsis in [coroutine.handle]:
template <> struct coroutine_handle<noop_coroutine_promise> : coroutine_handle<> { // 18.11.2.7 noop observers constexpr explicit operator bool() const noexcept; constexpr bool done() const noexcept; // 18.11.2.8 noop resumption constexpr void operator()() const noexcept; constexpr void resume() const noexcept; constexpr void destroy() const noexcept; // 18.11.2.9 noop promise access noop_coroutine_promise& promise() const noexcept; // 18.11.2.10 noop address constexpr void* address() const noexcept; private: coroutine_handle(unspecified); };
Add subclause 18.11.2.7 [coroutine.handle.noop.observers]:
18.11.2.7
noop_coroutine_handle
observers [coroutine.handle.noop.observers]:constexpr explicit operator bool() const noexcept;
1 Returns:
true
constexpr bool done() const noexcept;
2 Returns:
false
Add subclause 18.11.2.8 [coroutine.handle.noop.resumption]:
18.11.2.8
noop_coroutine_handle
resumption [coroutine.handle.noop.resumption]:constexpr void operator()() const noexcept; constexpr void resume() const noexcept; constexpr void destroy() const noexcept;1 Effects: None.
2 Remarks: Ifnoop_coroutine_handle
is converted tocoroutine_handle<>
, calls tooperator()
,resume
anddestroy
on that handle will also have no observable effects.
Add subclause 18.11.2.9 [coroutine.handle.noop.promise]:
18.11.2.9
noop_coroutine_handle
promise access [coroutine.handle.noop.promise]:noop_coroutine_promise& promise() const noexcept;
1 Returns: a reference to the promise object associated with this coroutine handle.
Add subclause 18.11.2.10 [coroutine.handle.noop.address]:
18.11.2.10
noop_coroutine_handle
address [coroutine.handle.noop.address]1 Returns:
constexpr void* address() const noexcept;
ptr
.
2 Remarks: A noop coroutine'sptr
always contains a non-null pointer value.
Add subclause 18.11.4 [coroutine.promise.noop]:
18.11.4 Class
noop_coroutine_promise
[coroutine.promise.noop]
struct noop_coroutine_promise{};
1 The class
noop_coroutine_promise
defines the promise type for the coroutine referred to bynoop_coroutine_handle
(18.11.5).
Add subclause 18.11.5 [coroutine.noop]:
18.11.5 Function
noop_coroutine
[coroutine.noop]noop_coroutine_handle noop_coroutine() noexcept;
1 Returns: A handle to a coroutine that has no observable effects when resumed or destroyed.
2 Remarks: A handle returned from
noop_coroutine
may or may not compare equal to a handle returned from another invocation ofnoop_coroutine
.