Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion include/boost/capy/concept/io_runnable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <boost/capy/detail/config.hpp>
#include <boost/capy/concept/io_awaitable.hpp>
#include <boost/capy/ex/io_env.hpp>

#include <coroutine>
#include <exception>
Expand All @@ -37,6 +38,8 @@ namespace capy {
@li `t.release()` releases ownership, must be `noexcept`
@li `p.exception()` returns `std::exception_ptr`, must be `noexcept`
@li `p.result()` returns the task result (required for non-void tasks)
@li `p.set_continuation(h)` sets the continuation handle, must be `noexcept`
@li `p.set_environment(env)` sets the execution environment, must be `noexcept`

@par Semantic Requirements

Expand All @@ -61,6 +64,18 @@ namespace capy {
@li Returns the value passed to `co_return`
@li Behavior is undefined if called when `exception()` is non-null

The `set_continuation` operation establishes the continuation:

@li Sets the coroutine handle to resume when this task reaches
`final_suspend`
@li Used by launch functions to wire the task back to the trampoline

The `set_environment` operation establishes the execution environment:

@li Sets the `io_env` pointer that propagates executor, stop token,
and allocator through the coroutine chain
@li The pointed-to `io_env` must outlive the coroutine

@par Conforming Signatures

@code
Expand All @@ -71,6 +86,8 @@ namespace capy {
{
std::exception_ptr exception() noexcept;
R result(); // non-void tasks only
void set_continuation(std::coroutine_handle<>) noexcept;
void set_environment(io_env const*) noexcept;
};

std::coroutine_handle<promise_type> handle() const noexcept;
Expand All @@ -84,11 +101,13 @@ template<typename T>
concept IoRunnable =
IoAwaitable<T> &&
requires { typename T::promise_type; } &&
requires(T& t, T const& ct, typename T::promise_type const& cp)
requires(T& t, T const& ct, typename T::promise_type const& cp, typename T::promise_type& p)
{
{ ct.handle() } noexcept -> std::same_as<std::coroutine_handle<typename T::promise_type>>;
{ cp.exception() } noexcept -> std::same_as<std::exception_ptr>;
{ t.release() } noexcept;
{ p.set_continuation(std::coroutine_handle<>{}) } noexcept;
{ p.set_environment(static_cast<io_env const*>(nullptr)) } noexcept;
} &&
(std::is_void_v<decltype(std::declval<T&>().await_resume())> ||
requires(typename T::promise_type& p) {
Expand Down
11 changes: 11 additions & 0 deletions test/unit/task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// Test that header file is self-contained.
#include <boost/capy/task.hpp>

#include <boost/capy/ex/io_env.hpp>
#include <boost/capy/ex/run_async.hpp>
#include <boost/capy/ex/this_coro.hpp>
#include <boost/capy/test/run_blocking.hpp>
Expand All @@ -33,6 +34,16 @@ static_assert(IoAwaitable<task<int>>);
static_assert(IoRunnable<task<void>>);
static_assert(IoRunnable<task<int>>);

// Verify set_continuation/set_environment are part of IoRunnable
static_assert(requires(task<void>::promise_type& p) {
{ p.set_continuation(std::coroutine_handle<>{}) } noexcept;
{ p.set_environment(static_cast<io_env const*>(nullptr)) } noexcept;
});
static_assert(requires(task<int>::promise_type& p) {
{ p.set_continuation(std::coroutine_handle<>{}) } noexcept;
{ p.set_environment(static_cast<io_env const*>(nullptr)) } noexcept;
});

/** Tracking executor that logs dispatch calls with an ID.
Uses pointers to external storage to allow copying.
*/
Expand Down
Loading