Skip to content

Add subsections to the "Design - User side" section describing sender/awaitable interop#181

Merged
ericniebler merged 10 commits intomainfrom
design-for-coro-sender-interop
Sep 30, 2021
Merged

Add subsections to the "Design - User side" section describing sender/awaitable interop#181
ericniebler merged 10 commits intomainfrom
design-for-coro-sender-interop

Conversation

@ericniebler
Copy link
Collaborator

fixes #167

@ericniebler ericniebler changed the base branch from main to senders-are-awaitable August 27, 2021 19:01
@ericniebler ericniebler changed the title Add subsections to the "Design - User side" section describing sender/adaptor interop Add subsections to the "Design - User side" section describing sender/awaitable interop Aug 27, 2021
std_execution.bs Outdated
Comment on lines +676 to +677
In truth there will be no problem because all awaitable types automatically model the `typed_sender` concept. The adaptation is transparent and happens in the sender customization points, which are aware of awaitables.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"all awaitable types"

You need to be careful and precise what you mean when you say "awaitable" here.
I found there was much confusion about the subtle nuances in what we mean when we say "awaitable".

There can be awaitable types that are only awaitable within certain contexts.
e.g. many of the folly::coro awaitable types extract some information from the awaiting coroutine's promise via the coroutine_handle passed to await_suspend(). You can't just pass any old coroutine_handle to such an awaitable and expect it to work here.

In the P1288 paper the awaitable concept required that await_suspend() was invocable with coroutine_handle<void> as a proxy for accepting any coroutine_handle and thus being awaitable within any natural coroutine-context (i.e. without await_transform).

}
```

Since awaitables are senders, writing a sender-based asynchronous algorithm is trivial if you have a coroutine task type: implement the algorithm as a coroutine. If you are not bothered by the possibility of allocations and indirections as a result of using coroutines, then there is no need to ever write a sender, a receiver, or an operation state.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is no need to ever write a sender

This comes with the proviso that there are sufficient concurrency building-blocks available that you can use.
Not all algorithms can be implemented purely with coroutines. Having said that, most algorithms can be written using coroutines once you have something like async_scope that can spawn operations that execute concurrently - allowing structured concurrency algorithms to be built out of the ad-hoc concurrency mechanisms.

std_execution.bs Outdated
<pre highlight="c++">
template &lt;class S>
requires <i>single-typed-sender</i>&lt;S&> // See <a href="#spec-execution.coro_utils.as_awaitable">[execution.coro_utils.as_awaitable]</a>
task&lt;<i>single-sender-value-type</i>&lt;S>> retry(S& s) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be pass-by-value to avoid the dangling-reference problems?

This algorithm would be difficult to use in a sender composition expression as it won't accept temporary senders as arguments.

std_execution.bs Outdated

When your task type's promise inherits from `with_awaitable_senders`, what happens is this: the coroutine behaves as if an *uncatchable exception* had been thrown from the `co_await` expression. (It is not really an exception, but it's helpful to think of it that way.) Provided that the promise types of the calling coroutines also inherit from `with_awaitable_senders`, or more generally implement a member function called `unhandled_done`, the exception unwinds the chain of coroutines as if an exception were thrown except that it bypasses `catch(...)` clauses.

In order to "catch" this uncatchable done exception, one of the calling coroutines in the stack would have to await a sender that maps the done channel into either a value or an error. That is achievable with either the `execution::let_done` or `execution::upon_done` sender adaptors.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure that we should be recommending let_done here.
It's use within a coroutine is complicated as having the let_done() return a just(std::nullopt_t) will result in the overall sender having multiple result-types and then needing to be piped through into_variant.

It would be nicer if we could describe an done_as_optional algorithm and show an example of its use...

std::optional<T> result = co_await done_as_optional(some_operation(args...));
if (!result) {
  // completed with set_done()
} else {
  // completed with set_value()
  auto& value = *result;
}

Whereas doing the same thing with let_done() is more cumbersome:

std::optional<T> result = co_await transform(
  let_done(some_operation(args...), []() noexcept { return just(std::nullopt); }),
  [](auto&& value) -> std::optional<T> { return static_cast<decltype(value)>(value); });

Maybe also throw in an example of using done_as_error<E>()?

try {
  T result = co_await done_as_error<operation_cancelled>(some_operation(args...));
} catch (operation_cancelled) {
  ...
}

Or alternatively show that you can just let this exception propagate up through the call-stack of coroutines...

Base automatically changed from senders-are-awaitable to main September 30, 2021 16:43
…r-interop

# Conflicts:
#	include/execution.hpp
#	std_execution.bs
@ericniebler ericniebler merged commit 34762ee into main Sep 30, 2021
@ericniebler ericniebler deleted the design-for-coro-sender-interop branch September 30, 2021 18:03
github-actions bot pushed a commit that referenced this pull request Sep 30, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants