diff --git a/.cursor/commands/doc-rebuild.md b/.cursor/commands/doc-rebuild.md index fb5bb07b1..c257f66d0 100644 --- a/.cursor/commands/doc-rebuild.md +++ b/.cursor/commands/doc-rebuild.md @@ -25,7 +25,7 @@ Regenerate the Capy documentation following this structure and style guide. - Protocols — no HTTP, WebSocket, or TLS; see the Http and Beast2 libraries - Platform event loops — no io_uring, IOCP, epoll, or kqueue; Capy is the layer above - Callbacks or futures — coroutine-only means no other continuation styles - - Sender/receiver — Capy uses the IoAwaitable protocol, not `std::execution` + - Sender/receiver — Capy uses the ContextAwaitable protocol, not `std::execution` 4. **Target Audience** (verbatim): - Users of Corosio — portable coroutine networking - Users of Http — sans-I/O HTTP/1.1 clients and servers @@ -153,10 +153,10 @@ This section transitions from general C++ knowledge to Capy-specific library usa - `execution_context` — base class for custom contexts - `strand` — serialization without mutexes - Single-threaded vs multi-threaded patterns -- **The IoAwaitable Protocol** (`coroutines/io-awaitable.adoc`) +- **The ContextAwaitable Protocol** (`coroutines/context-awaitable.adoc`) - The three-argument `await_suspend` signature - Forward context propagation vs backward queries - - `IoAwaitable`, `IoAwaitableTask`, `IoLaunchableTask` concepts + - `ContextAwaitable` and `ContextRunnable` concepts - Why affinity matters for I/O - **Stop Tokens and Cancellation** (`coroutines/cancellation.adoc`) **Teach from the ground up for complete beginners:** @@ -215,7 +215,7 @@ This section transitions from general C++ knowledge to Capy-specific library usa - Capy's answer: stop tokens flow downward through `co_await` - `get_stop_token()` — retrieve the current stop token inside a task - Automatic propagation: child tasks inherit parent's stop token - - No manual threading: the IoAwaitable protocol handles it + - No manual threading: the ContextAwaitable protocol handles it *Part 4: Responding to Cancellation* - Checking: `if (token.stop_requested()) co_return;` - Cleanup: RAII ensures resources are released on early exit @@ -248,7 +248,7 @@ This section transitions from general C++ knowledge to Capy-specific library usa - ``, `` - ``, `` - ``, `` -- ``, `` +- ``, `` - `` - ``, `` diff --git a/.cursor/rules/code.mdc b/.cursor/rules/code.mdc index e036386cd..4356b8a01 100644 --- a/.cursor/rules/code.mdc +++ b/.cursor/rules/code.mdc @@ -6,4 +6,4 @@ alwaysApply: true - test objects usually construct in fuse lambda - `task<>` for `task` -- awaitables satisfy concept IoAwaitable +- awaitables satisfy concept ContextAwaitable diff --git a/.cursor/rules/doc-awaitable.mdc b/.cursor/rules/doc-awaitable.mdc index 5ea863a93..3bba5f6fb 100644 --- a/.cursor/rules/doc-awaitable.mdc +++ b/.cursor/rules/doc-awaitable.mdc @@ -25,7 +25,7 @@ Revised checklist: - **`@return`** — Describe the returned aggregate and its elements. The first element is always `error_code`. Name and describe each subsequent element (e.g. "bytes_transferred", "endpoint"). Note that the result is customarily destructured by the caller. - **Error conditions** — Document the notable `error_code` values or conditions that may appear in the first element. E.g. `capy::cond::canceled` if the stop token was activated or the i/o object's `cancel()` was called, EOF conditions, protocol-specific errors, etc. State that error codes should be compared to error conditions, not specific values. - **`@throws`** — Typically only for precondition violations. State which preconditions trigger exceptions, or state that no exceptions are thrown during normal operation. -- **Cancellation** — State that the operation supports cancellation via `stop_token` propagated through the IoAwaitable protocol, or via the i/o object's `cancel()` member. State that the resulting error compares equal to `capy::cond::canceled`. +- **Cancellation** — State that the operation supports cancellation via `stop_token` propagated through the ContextAwaitable protocol, or via the i/o object's `cancel()` member. State that the resulting error compares equal to `capy::cond::canceled`. - **`@par Example`** — Two or three `@code` blocks showing different usage patterns: typical happy path with destructuring, error handling, cancellation, different overloads, etc. - **`@note` / `@par Remarks`** — Behavioral gotchas. E.g. "This operation may not read all of the requested bytes." Or equivalence to another overload. - **`@tparam`** — For non-variadic template parameters. State the concept requirement (e.g. "The type must satisfy the *AsyncStream* concept"). Don't document `Args...`. diff --git a/.cursor/rules/doc-concept.mdc b/.cursor/rules/doc-concept.mdc index a13f398fe..7850199fa 100644 --- a/.cursor/rules/doc-concept.mdc +++ b/.cursor/rules/doc-concept.mdc @@ -14,9 +14,9 @@ alwaysApply: false - **Brief** — One sentence naming the concept and stating what a conforming type does. E.g. "Concept for types that consume buffer data using callee-owned buffers." - **Extended description** — A short paragraph summarizing the pattern the concept models, its purpose, and how it contrasts with related concepts. - **`@tparam`** — One entry per template parameter to the concept. -- **`@par Syntactic Requirements`** — Bulleted `@li` list of every expression that must be valid. For each, state the member name, its arguments, and what it returns (including whether the return type must satisfy another concept like `IoAwaitable`). State the decomposed result tuple. +- **`@par Syntactic Requirements`** — Bulleted `@li` list of every expression that must be valid. For each, state the member name, its arguments, and what it returns (including whether the return type must satisfy another concept like `ContextAwaitable`). State the decomposed result tuple. - **`@par Semantic Requirements`** — Grouped by operation. For each operation, a bulleted `@li` list describing its behavior, preconditions, postconditions, success/error semantics, and any state transitions (e.g. "no further operations are permitted after EOF"). - **`@par Buffer Lifetime`** (or equivalent resource lifetime section) — If the concept involves borrowed resources (buffers, handles, views), state exactly when they are valid and what invalidates them. -- **`@par Conforming Signatures`** — A single `@code` block showing the canonical signatures using pseudo-syntax (e.g. `IoAwaitable auto`). This is the quick-reference that implementers copy from. +- **`@par Conforming Signatures`** — A single `@code` block showing the canonical signatures using pseudo-syntax (e.g. `ContextAwaitable auto`). This is the quick-reference that implementers copy from. - **`@par Example`** — A `@code` block showing a generic algorithm templated on the concept, demonstrating typical usage with destructuring, error handling, and interaction with related concepts. - **`@see`** — Always last. Cross-references to related concepts, concrete models, and foundational concepts the signatures depend on. diff --git a/.cursor/rules/javadoc.mdc b/.cursor/rules/javadoc.mdc index e25126cf9..e991794f3 100644 --- a/.cursor/rules/javadoc.mdc +++ b/.cursor/rules/javadoc.mdc @@ -4,5 +4,5 @@ globs: alwaysApply: true --- -- Return io_result documented as "An awaitable yielding `(error_code, T1 ,T2, T3)`" ONLY type names (no inside spaces) -- io_result params (T1,T2,T3) meaning explained last in description +- Return `results` documented as "An awaitable yielding `(error_code, T1 ,T2, T3)`" ONLY type names (no inside spaces) +- `results` params (T1,T2,T3) meaning explained last in description diff --git a/README.md b/README.md index 0444fc996..29dee9c53 100644 --- a/README.md +++ b/README.md @@ -50,14 +50,14 @@ Capy is also used by [Beast2](https://github.com/cppalliance/beast2), which uses The Beast2 family of libraries includes: -- **Capy** — The foundation of I/O. -- **Corosio** — Coroutine-only portable networking. This is the successor to Boost.Asio. -- **Http** — Sans-I/O HTTP/1.1. This is a high-level library: servers, clients, Express JS middleware. -- **Websocket** — Sans-I/O Websocket. This is also a high-level library. -- **Beast2** — High-level HTTP and WebSocket servers. Express.js-style routing, multithreaded, idiomatic C++. -- **Burl** — High-level HTTP client. The features of curl, the ergonomics of coroutines, and the design sensibility of Python Requests. +- **[Capy](https://github.com/cppalliance/capy)** — The foundation of I/O. +- **[Corosio](https://github.com/cppalliance/corosio)** — Coroutine-only portable networking. This is the successor to Boost.Asio. +- **[Http](https://github.com/cppalliance/http)** — Sans-I/O HTTP/1.1. This is a high-level library: servers, clients, Express JS middleware. +- **[Websocket](https://github.com/cppalliance/websocket)** — Sans-I/O Websocket. This is also a high-level library. +- **[Beast2](https://github.com/cppalliance/beast2)** — High-level HTTP and WebSocket servers. Express.js-style routing, multithreaded, idiomatic C++. +- **[Burl](https://github.com/cppalliance/burl)** — High-level HTTP client. The features of curl, the ergonomics of coroutines, and the design sensibility of Python Requests. -Currently, the C++ Standard does not deliver facilities optimized for networking I/O. We believe that Capy should become a standard library component to fill this gap. Our first paper based on Capy introduces the _IoAwaitable_ family of concepts: +Currently, the C++ Standard does not deliver facilities optimized for networking I/O. We believe that Capy should become a standard library component to fill this gap. Our first paper based on Capy introduces the _IoAwaitable_ family of concepts, which Capy now names _ContextAwaitable_: - Paper: https://github.com/cppalliance/wg21-papers/blob/master/source/_reserve/d4003-io-awaitables.md @@ -71,7 +71,7 @@ Capy represents Boost returning to its role as a leader in C++ standardization e When an I/O operation completes, the operating system wakes up some thread, such as a completion port thread, an epoll reactor, or an io_uring worker. Without affinity tracking, your coroutine resumes on that arbitrary thread, forcing you to add synchronization everywhere or risk data races. This is the fundamental problem that coroutine-based networking must solve. -Capy's answer is the _IoAwaitable_ protocol. When you launch a coroutine with a designated executor, every child coroutine inherits that executor affinity automatically. Execution context flows forward through `co_await` chains, not backward through P3826 queries, to ensure every coroutine in the chain runs in the same context. When I/O completes on some OS thread, the _IoAwaitable_ protocol ensures your coroutine resumes on its designated executor. The data flow is explicit and testable. There are no thread-local globals, no implicit context, no surprises. Capy's `task` type uses the compiler to enforce invariants. +Capy's answer is the _ContextAwaitable_ protocol. When you launch a coroutine with a designated executor, every child coroutine inherits that executor affinity automatically. Execution context flows forward through `co_await` chains, not backward through P3826 queries, to ensure every coroutine in the chain runs in the same context. When I/O completes on some OS thread, the _ContextAwaitable_ protocol ensures your coroutine resumes on its designated executor. The data flow is explicit and testable. There are no thread-local globals, no implicit context, no surprises. Capy's `task` type uses the compiler to enforce invariants. Cancellation follows the same forward-propagation model. Stop tokens flow forward from the launch of a coroutine chain alongside the execution context, to arrive at the platform API boundary, providing a uniform cancellation interface across all operations. @@ -85,7 +85,7 @@ Capy is opinionated on the things that matter for I/O: - Stop token integration: uniform cancellation, always available - Allocator control over frame allocation with zero-overhead recycling - Forward propagation of the full context through every `co_await` -- A `task` type that enforces the _IoAwaitable_ protocol at compile time +- A `task` type that enforces the _ContextAwaitable_ protocol at compile time - Composition primitives for launching and coordinating coroutines - A strand for safe concurrency without mutexes - Buffer sequences: `std::ranges` for untyped bytes diff --git a/bench/CMakeLists.txt b/bench/CMakeLists.txt index 8cfa90eda..f948df506 100644 --- a/bench/CMakeLists.txt +++ b/bench/CMakeLists.txt @@ -13,11 +13,11 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX "" FILES stdexec/main.cpp stdexec/allocation_tracker.hpp stdexec/awaitable_sender.hpp - stdexec/ioaw_io_read_stream.hpp - stdexec/ioaw_read_stream.hpp - stdexec/ioaw_sync_read_stream.hpp + stdexec/ctxaw_io_read_stream.hpp + stdexec/ctxaw_read_stream.hpp + stdexec/ctxaw_sync_read_stream.hpp stdexec/sender_awaitable.hpp - stdexec/sender_io_env.hpp + stdexec/sender_await_context.hpp stdexec/sndr_any_read_stream.hpp stdexec/sndr_io_read_stream.hpp stdexec/sndr_read_stream.hpp diff --git a/bench/beman/awaitable_sender.hpp b/bench/beman/awaitable_sender.hpp index 07eb0f701..4b3d941e1 100644 --- a/bench/beman/awaitable_sender.hpp +++ b/bench/beman/awaitable_sender.hpp @@ -10,11 +10,11 @@ #ifndef BOOST_CAPY_BENCH_AWAITABLE_SENDER_HPP #define BOOST_CAPY_BENCH_AWAITABLE_SENDER_HPP -#include +#include #include +#include #include -#include -#include +#include #include @@ -31,7 +31,7 @@ namespace boost::capy { // Query CPO for obtaining a Capy-compatible executor // from a P2300 environment. The returned object must // satisfy Capy's Executor concept. Environments that -// host IoAwaitables via the as_sender_lossy bridge must +// host ContextAwaitables via the as_sender_lossy bridge must // answer this query. struct get_io_executor_t { @@ -109,24 +109,24 @@ struct frame_cb } // namespace detail -/** Sender that wraps an IoAwaitable. +/** Sender that wraps a ContextAwaitable. When connected or co_awaited, the bridge queries the receiver's or promise's environment for a Capy-compatible executor via get_io_executor. The executor is stored by value in the operation - state and used to construct the io_env passed to - the IoAwaitable's await_suspend. + state and used to construct the await_context passed to + the ContextAwaitable's await_suspend. - @tparam IoAw The IoAwaitable type. + @tparam CtxAw The ContextAwaitable type. */ -template +template struct awaitable_sender { using sender_concept = beman::execution::sender_t; using result_type = decltype( - std::declval&>().await_resume()); + std::declval&>().await_resume()); static auto make_sigs() { @@ -159,7 +159,7 @@ struct awaitable_sender using completion_signatures = decltype(make_sigs()); - IoAw aw_; + CtxAw aw_; template struct op_state @@ -173,13 +173,13 @@ struct awaitable_sender std::declval())) .query(get_io_executor_t{})); - IoAw aw_; + CtxAw aw_; Receiver rcvr_; executor_type ex_; - io_env env_; + await_context env_; detail::frame_cb cb_; - op_state(IoAw aw, Receiver rcvr) + op_state(CtxAw aw, Receiver rcvr) : aw_(std::move(aw)) , rcvr_(std::move(rcvr)) , ex_{} @@ -299,7 +299,7 @@ struct awaitable_sender beman::execution::get_stop_token_t{}); } - env_ = io_env{ex_, st, nullptr}; + env_ = await_context{ex_, st, nullptr}; if(aw_.await_ready()) { @@ -337,7 +337,7 @@ struct awaitable_sender } // Bypass beman's sender_awaitable when co_awaited - // from a bex::task. Adapts the IoAwaitable's 2-arg + // from a bex::task. Adapts the ContextAwaitable's 2-arg // await_suspend to standard 1-arg protocol, avoiding // the double bridge (as_sender_lossy + sender_awaitable). template @@ -362,10 +362,10 @@ struct awaitable_sender struct aw { - IoAw aw_; + CtxAw aw_; executor_type ex_; std::stop_token st_; - io_env env_; + await_context env_; bool await_ready() noexcept { @@ -375,7 +375,7 @@ struct awaitable_sender std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) { - env_ = io_env{ex_, st_, nullptr}; + env_ = await_context{ex_, st_, nullptr}; return aw_.await_suspend(h, &env_); } @@ -389,7 +389,7 @@ struct awaitable_sender } }; -/** Create a beman::execution sender from an IoAwaitable. +/** Create a beman::execution sender from a ContextAwaitable. The bridge routes the awaitable's result through sender channels based on its type: @@ -410,21 +410,21 @@ struct awaitable_sender via get_io_executor. The environment must answer this query with an object satisfying Capy's Executor concept. - @param aw The IoAwaitable to wrap. + @param aw The ContextAwaitable to wrap. @return A sender whose completion channels reflect the awaitable's result type. */ // Benchmark-only bridge. Unlike the canonical as_sender in -// example/awaitable-sender, this one accepts a compound io_result and +// example/awaitable-sender, this one accepts a compound `results` and // splits it at runtime: set_value(n) on success, set_error(ec) on // failure, dropping the byte count that accompanied the error. The // benchmark keeps it to measure the bridge without the task // wrapper the canonical bridge requires; do not copy it into examples. -template -auto as_sender_lossy(IoAw&& aw) +template +auto as_sender_lossy(CtxAw&& aw) { - return awaitable_sender>{ - std::forward(aw)}; + return awaitable_sender>{ + std::forward(aw)}; } // ------------------------------------------------------- diff --git a/bench/beman/ctxaw_io_read_stream.hpp b/bench/beman/ctxaw_io_read_stream.hpp new file mode 100644 index 000000000..82a474026 --- /dev/null +++ b/bench/beman/ctxaw_io_read_stream.hpp @@ -0,0 +1,36 @@ +// +// Copyright (c) 2026 Steve Gerbino +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/capy +// + +#ifndef BOOST_CAPY_BENCH_CTXAW_IO_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_CTXAW_IO_READ_STREAM_HPP + +#include "ctxaw_read_stream.hpp" + +/// Abstract interface for ContextAwaitable read streams. +struct ctxaw_io_read_stream +{ + virtual ctxaw_read_stream::read_awaitable + read_some(boost::capy::mutable_buffer) = 0; + virtual ~ctxaw_io_read_stream() = default; +}; + +/// Concrete implementation of ctxaw_io_read_stream wrapping +/// an ctxaw_read_stream. +struct ctxaw_io_read_stream_impl : ctxaw_io_read_stream +{ + ctxaw_read_stream stream_; + + ctxaw_read_stream::read_awaitable + read_some(boost::capy::mutable_buffer buf) override + { + return stream_.read_some(buf); + } +}; + +#endif diff --git a/bench/stdexec/ioaw_read_stream.hpp b/bench/beman/ctxaw_read_stream.hpp similarity index 73% rename from bench/stdexec/ioaw_read_stream.hpp rename to bench/beman/ctxaw_read_stream.hpp index cd76367d9..97da92e97 100644 --- a/bench/stdexec/ioaw_read_stream.hpp +++ b/bench/beman/ctxaw_read_stream.hpp @@ -7,23 +7,23 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_BENCH_STDEXEC_IOAW_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_STDEXEC_IOAW_READ_STREAM_HPP +#ifndef BOOST_CAPY_BENCH_CTXAW_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_CTXAW_READ_STREAM_HPP #include #include #include -#include -#include +#include +#include #include #include /// No-op ReadStream for benchmarking. /// -/// Uses the executor from io_env (passed by capy::task's +/// Uses the executor from await_context (passed by capy::task's /// transform_awaiter) to post the coroutine back. Satisfies /// ReadStream so it can be wrapped by capy::any_read_stream. -struct ioaw_read_stream +struct ctxaw_read_stream { struct read_awaitable { @@ -34,14 +34,14 @@ struct ioaw_read_stream std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - boost::capy::io_env const* env) + boost::capy::await_context const* env) { cont_.h = h; env->executor.post(cont_); return std::noop_coroutine(); } - boost::capy::io_result + boost::capy::results await_resume() noexcept { return {std::error_code(), 0}; } }; @@ -52,6 +52,6 @@ struct ioaw_read_stream } }; -static_assert(boost::capy::ReadStream); +static_assert(boost::capy::ReadStream); #endif diff --git a/bench/beman/ioaw_sync_read_stream.hpp b/bench/beman/ctxaw_sync_read_stream.hpp similarity index 73% rename from bench/beman/ioaw_sync_read_stream.hpp rename to bench/beman/ctxaw_sync_read_stream.hpp index 0a75aeef1..80311e68c 100644 --- a/bench/beman/ioaw_sync_read_stream.hpp +++ b/bench/beman/ctxaw_sync_read_stream.hpp @@ -8,7 +8,7 @@ // // -// Synchronous-completion IoAwaitable stream. +// Synchronous-completion ContextAwaitable stream. // // Every read completes immediately via symmetric // transfer — await_suspend returns the coroutine @@ -16,16 +16,16 @@ // round-trip. // -#ifndef BOOST_CAPY_BENCH_IOAW_SYNC_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_IOAW_SYNC_READ_STREAM_HPP +#ifndef BOOST_CAPY_BENCH_CTXAW_SYNC_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_CTXAW_SYNC_READ_STREAM_HPP -#include -#include +#include +#include #include #include -struct ioaw_sync_read_stream +struct ctxaw_sync_read_stream { struct read_awaitable { @@ -37,13 +37,13 @@ struct ioaw_sync_read_stream std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - boost::capy::io_env const*) + boost::capy::await_context const*) { // Data already buffered — resume inline return h; } - boost::capy::io_result + boost::capy::results await_resume() noexcept { return {std::error_code(), 0}; diff --git a/bench/beman/ioaw_io_read_stream.hpp b/bench/beman/ioaw_io_read_stream.hpp deleted file mode 100644 index beb819fa3..000000000 --- a/bench/beman/ioaw_io_read_stream.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/capy -// - -#ifndef BOOST_CAPY_BENCH_IOAW_IO_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_IOAW_IO_READ_STREAM_HPP - -#include "ioaw_read_stream.hpp" - -/// Abstract interface for IoAwaitable read streams. -struct ioaw_io_read_stream -{ - virtual ioaw_read_stream::read_awaitable - read_some(boost::capy::mutable_buffer) = 0; - virtual ~ioaw_io_read_stream() = default; -}; - -/// Concrete implementation of ioaw_io_read_stream wrapping -/// an ioaw_read_stream. -struct ioaw_io_read_stream_impl : ioaw_io_read_stream -{ - ioaw_read_stream stream_; - - ioaw_read_stream::read_awaitable - read_some(boost::capy::mutable_buffer buf) override - { - return stream_.read_some(buf); - } -}; - -#endif diff --git a/bench/beman/main.cpp b/bench/beman/main.cpp index 1fc73c626..09c6366e7 100644 --- a/bench/beman/main.cpp +++ b/bench/beman/main.cpp @@ -25,16 +25,16 @@ #include "allocation_tracker.hpp" #include "awaitable_sender.hpp" -#include "ioaw_read_stream.hpp" -#include "ioaw_io_read_stream.hpp" +#include "ctxaw_read_stream.hpp" +#include "ctxaw_io_read_stream.hpp" #include "repeat_until.hpp" #include "sender_awaitable.hpp" #include "sndr_any_read_stream.hpp" #include "sndr_io_read_stream.hpp" #include "sndr_read_stream.hpp" #include "sndr_sync_read_stream.hpp" -#include "ioaw_sync_read_stream.hpp" -#include "sender_io_env.hpp" +#include "ctxaw_sync_read_stream.hpp" +#include "sender_await_context.hpp" #include #include @@ -91,7 +91,7 @@ static constexpr int BRIDGED_EXEC_MODEL = 1; // Table 1: capy::task // // Templated session/accept coroutines instantiated with each -// stream type. The executor comes from io_env via capy::task's +// stream type. The executor comes from await_context via capy::task's // transform_awaiter. // =================================================================== @@ -124,7 +124,7 @@ capy::task<> capy_accept(Stream& stream, cell_result& out) // Table 1: capy::task — Column B (sender via await_sender bridge) // // The stream returns a sender. capy::task consumes it by wrapping -// in await_sender which bridges the sender to an IoAwaitable. +// in await_sender which bridges the sender to a ContextAwaitable. // Single pool: sender_thread_pool with sender_as_capy_executor // adapter so capy::task can run on it. // =================================================================== @@ -158,7 +158,7 @@ capy::task<> capy_accept_sndr(Stream& stream, cell_result& out) // =================================================================== // Table 2: bex::task — Column A (sender, native) // -// Same templated pattern but using bex::task coroutines on +// Same templated pattern but using bex::task coroutines on // sender_thread_pool. // =================================================================== @@ -166,7 +166,7 @@ template auto bex_session( Stream& stream, std::allocator_arg_t, - std::pmr::polymorphic_allocator) -> bex::task + std::pmr::polymorphic_allocator) -> bex::task { char buf[64]; for (int i = 0; i < INNER_LOOPS; ++i) @@ -179,7 +179,7 @@ auto bex_accept( Stream& stream, cell_result& out, std::allocator_arg_t, - std::pmr::polymorphic_allocator alloc) -> bex::task + std::pmr::polymorphic_allocator alloc) -> bex::task { auto before = g_alloc_count.load(std::memory_order_relaxed); auto start = std::chrono::steady_clock::now(); @@ -197,15 +197,15 @@ auto bex_accept( // =================================================================== // Table 2: bex::task — Column B (awaitable via as_sender_lossy bridge) // -// The stream returns an IoAwaitable. bex::task consumes it by +// The stream returns a ContextAwaitable. bex::task consumes it by // wrapping in as_sender_lossy which bridges the awaitable to a sender. // =================================================================== template -auto bex_session_ioaw( +auto bex_session_ctxaw( Stream& stream, std::allocator_arg_t, - std::pmr::polymorphic_allocator) -> bex::task + std::pmr::polymorphic_allocator) -> bex::task { char buf[64]; for (int i = 0; i < INNER_LOOPS; ++i) @@ -215,17 +215,17 @@ auto bex_session_ioaw( } template -auto bex_accept_ioaw( +auto bex_accept_ctxaw( Stream& stream, cell_result& out, std::allocator_arg_t, - std::pmr::polymorphic_allocator alloc) -> bex::task + std::pmr::polymorphic_allocator alloc) -> bex::task { auto before = g_alloc_count.load(std::memory_order_relaxed); auto start = std::chrono::steady_clock::now(); for (int i = 0; i < OUTER_LOOPS; ++i) - co_await bex_session_ioaw(stream, + co_await bex_session_ctxaw(stream, std::allocator_arg, alloc); auto elapsed = std::chrono::steady_clock::now() - start; @@ -344,10 +344,10 @@ int main() // Col B: Awaitable (via as_sender_lossy bridge) - // Native — ioaw_read_stream + // Native — ctxaw_read_stream { sender_thread_pool pool(1); - ioaw_read_stream stream; + ctxaw_read_stream stream; auto sched = pool.get_scheduler(); int count = OPS_PER_CELL; char buf[64]; @@ -372,10 +372,10 @@ int main() after - before}; } - // Abstract — ioaw_io_read_stream + // Abstract — ctxaw_io_read_stream { sender_thread_pool pool(1); - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; auto sched = pool.get_scheduler(); int count = OPS_PER_CELL; char buf[64]; @@ -386,7 +386,7 @@ int main() repeat_until( bex::let_value(bex::just(), [&]() { return capy::as_sender_lossy( - static_cast( + static_cast( stream).read_some( capy::mutable_buffer( buf, sizeof(buf)))); @@ -406,7 +406,7 @@ int main() // Type erased — capy::any_read_stream { sender_thread_pool pool(1); - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); auto sched = pool.get_scheduler(); int count = OPS_PER_CELL; @@ -461,10 +461,10 @@ int main() after - before}; } - // Synchronous — ioaw_sync_read_stream (Col B) + // Synchronous — ctxaw_sync_read_stream (Col B) { sender_thread_pool pool(1); - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; auto sched = pool.get_scheduler(); int count = OPS_PER_CELL; char buf[64]; @@ -494,21 +494,21 @@ int main() // --------------------------------------------------------------- - // Native — ioaw_read_stream + // Native — ctxaw_read_stream { capy::thread_pool pool(1); - ioaw_read_stream stream; + ctxaw_read_stream stream; capy::run_async(pool.get_executor())( capy_accept(stream, grid[run][CAPY_TASK][NATIVE_STREAM][NATIVE_EXEC_MODEL])); pool.join(); } - // Abstract — ioaw_io_read_stream + // Abstract — ctxaw_io_read_stream { capy::thread_pool pool(1); - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; capy::run_async(pool.get_executor())( - capy_accept(static_cast(stream), + capy_accept(static_cast(stream), grid[run][CAPY_TASK][ABSTRACT_STREAM][NATIVE_EXEC_MODEL])); pool.join(); } @@ -516,17 +516,17 @@ int main() // Type erased — capy::any_read_stream { capy::thread_pool pool(1); - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); capy::run_async(pool.get_executor())( capy_accept(stream, grid[run][CAPY_TASK][TYPE_ERASED_STREAM][NATIVE_EXEC_MODEL])); pool.join(); } - // Synchronous — ioaw_sync_read_stream + // Synchronous — ctxaw_sync_read_stream { capy::thread_pool pool(1); - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; capy::run_async(pool.get_executor())( capy_accept(stream, grid[run][CAPY_TASK][SYNC_STREAM][NATIVE_EXEC_MODEL])); pool.join(); @@ -577,7 +577,7 @@ int main() } // --------------------------------------------------------------- - // Table 3: beman::execution::task (bex::task) + // Table 3: beman::execution::task (bex::task) // --------------------------------------------------------------- @@ -640,29 +640,29 @@ int main() // Col B: Awaitable (via as_sender_lossy bridge) - // Native — ioaw_read_stream + // Native — ctxaw_read_stream { sender_thread_pool pool(1); - ioaw_read_stream stream; + ctxaw_read_stream stream; auto sched = pool.get_scheduler(); auto* mr = get_counting_resource(); bex::sync_wait(bex::starts_on(sched, - bex_accept_ioaw( + bex_accept_ctxaw( stream, grid[run][BEMAN_TASK][NATIVE_STREAM][BRIDGED_EXEC_MODEL], std::allocator_arg, std::pmr::polymorphic_allocator(mr)))); pool.join(); } - // Abstract — ioaw_io_read_stream + // Abstract — ctxaw_io_read_stream { sender_thread_pool pool(1); - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; auto sched = pool.get_scheduler(); auto* mr = get_counting_resource(); bex::sync_wait(bex::starts_on(sched, - bex_accept_ioaw( - static_cast(stream), + bex_accept_ctxaw( + static_cast(stream), grid[run][BEMAN_TASK][ABSTRACT_STREAM][BRIDGED_EXEC_MODEL], std::allocator_arg, std::pmr::polymorphic_allocator(mr)))); @@ -672,26 +672,26 @@ int main() // Type erased — capy::any_read_stream { sender_thread_pool pool(1); - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); auto sched = pool.get_scheduler(); auto* mr = get_counting_resource(); bex::sync_wait(bex::starts_on(sched, - bex_accept_ioaw( + bex_accept_ctxaw( stream, grid[run][BEMAN_TASK][TYPE_ERASED_STREAM][BRIDGED_EXEC_MODEL], std::allocator_arg, std::pmr::polymorphic_allocator(mr)))); pool.join(); } - // Synchronous — ioaw_sync_read_stream + // Synchronous — ctxaw_sync_read_stream { sender_thread_pool pool(1); - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; auto sched = pool.get_scheduler(); auto* mr = get_counting_resource(); bex::sync_wait(bex::starts_on(sched, - bex_accept_ioaw( + bex_accept_ctxaw( stream, grid[run][BEMAN_TASK][SYNC_STREAM][BRIDGED_EXEC_MODEL], std::allocator_arg, std::pmr::polymorphic_allocator(mr)))); diff --git a/bench/beman/sender_io_env.hpp b/bench/beman/sender_await_context.hpp similarity index 95% rename from bench/beman/sender_io_env.hpp rename to bench/beman/sender_await_context.hpp index 9e0df9e02..20bc9920c 100644 --- a/bench/beman/sender_io_env.hpp +++ b/bench/beman/sender_await_context.hpp @@ -12,11 +12,11 @@ // // Provides pool_scheduler (the P2300 scheduler for // sender_thread_pool), the capy executor adapter, and -// the io_env for beman::execution::task. +// the await_context for beman::execution::task. // -#ifndef BOOST_CAPY_BENCH_SENDER_IO_ENV_HPP -#define BOOST_CAPY_BENCH_SENDER_IO_ENV_HPP +#ifndef BOOST_CAPY_BENCH_SENDER_AWAIT_CONTEXT_HPP +#define BOOST_CAPY_BENCH_SENDER_AWAIT_CONTEXT_HPP #include "sender_thread_pool.hpp" #include "awaitable_sender.hpp" @@ -208,20 +208,20 @@ inline void sender_as_capy_executor::post( ex::start(p->op_); } -struct io_env +struct await_context { using scheduler_type = pool_scheduler; using allocator_type = std::pmr::polymorphic_allocator; sender_thread_pool* pool_ = nullptr; - io_env() = default; + await_context() = default; template requires requires(Env const& e) { pool_scheduler{ex::get_scheduler(e)}; } - io_env(Env const& e) + await_context(Env const& e) : pool_(pool_scheduler{ex::get_scheduler(e)}.pool_) {} }; diff --git a/bench/beman/sender_awaitable.hpp b/bench/beman/sender_awaitable.hpp index 94abb85e5..7f3762052 100644 --- a/bench/beman/sender_awaitable.hpp +++ b/bench/beman/sender_awaitable.hpp @@ -11,8 +11,8 @@ #define BOOST_CAPY_EXAMPLE_SENDER_AWAITABLE_HPP #include -#include -#include +#include +#include #include @@ -184,7 +184,7 @@ struct bridge_receiver /** Awaitable that bridges a beman::execution sender into a Capy coroutine. - Satisfies IoAwaitable. When co_awaited inside + Satisfies ContextAwaitable. When co_awaited inside a capy::task, connects the sender to a bridge receiver, starts the operation, and resumes the coroutine when the sender completes. @@ -199,7 +199,7 @@ struct bridge_receiver completion signatures at compile time. If the sender can complete with set_error(std::error_code), await_resume - returns io_result so the error code is a + returns results so the error code is a value, not an exception. Otherwise await_resume returns the value directly and genuine exceptions are rethrown. @@ -270,7 +270,7 @@ struct [[nodiscard]] sender_awaitable std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { ::new(op_buf_) op_state_type( beman::execution::connect( @@ -304,7 +304,7 @@ struct [[nodiscard]] sender_awaitable private: // Sender can complete with - // set_error(error_code). Return io_result + // set_error(error_code). Return `results` // so the error code is a value, not an // exception. auto await_resume_ec() @@ -320,13 +320,13 @@ struct [[nodiscard]] sender_awaitable { // stopped at index 4 if(result_.index() == 4) - return io_result<>{ + return results<>{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result<>{ + return results<>{ std::get<2>(result_)}; - return io_result<>{}; + return results<>{}; } else if constexpr ( std::tuple_size_v< @@ -335,13 +335,13 @@ struct [[nodiscard]] sender_awaitable using T = std::tuple_element_t< 0, value_tuple>; if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_)}; - return io_result{ + return results{ std::error_code(), std::get<0>( std::get<1>( @@ -350,13 +350,13 @@ struct [[nodiscard]] sender_awaitable else { if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_)}; - return io_result{ + return results{ std::error_code(), std::get<1>( std::move(result_))}; @@ -392,12 +392,12 @@ struct [[nodiscard]] sender_awaitable } }; -/** Create an IoAwaitable from a +/** Create a ContextAwaitable from a beman::execution sender. If the sender can complete with set_error(std::error_code), the returned - awaitable yields io_result so the error code + awaitable yields results so the error code is a value, not an exception. Otherwise the awaitable yields the value directly. @@ -414,7 +414,7 @@ struct [[nodiscard]] sender_awaitable @endcode @param sndr The sender to bridge. - @return An IoAwaitable that can be co_awaited + @return A ContextAwaitable that can be co_awaited in a capy::task. */ template diff --git a/bench/beman/sender_thread_pool.hpp b/bench/beman/sender_thread_pool.hpp index fc3f8f849..c474d3afa 100644 --- a/bench/beman/sender_thread_pool.hpp +++ b/bench/beman/sender_thread_pool.hpp @@ -11,7 +11,7 @@ // Minimal thread pool for sender benchmarks. // // sender_thread_pool is the execution context. -// pool_scheduler (defined in sender_io_env.hpp) +// pool_scheduler (defined in sender_await_context.hpp) // is the P2300 scheduler handle. // @@ -92,7 +92,7 @@ class sender_thread_pool sender_thread_pool(sender_thread_pool const&) = delete; sender_thread_pool& operator=(sender_thread_pool const&) = delete; - // Defined in sender_io_env.hpp after pool_scheduler + // Defined in sender_await_context.hpp after pool_scheduler pool_scheduler get_scheduler() noexcept; void enqueue(work_item* w) diff --git a/bench/beman/sndr_read_stream.hpp b/bench/beman/sndr_read_stream.hpp index 5b626e010..798766193 100644 --- a/bench/beman/sndr_read_stream.hpp +++ b/bench/beman/sndr_read_stream.hpp @@ -45,7 +45,7 @@ struct sndr_read_stream sender_thread_pool* pool_; - // awaitable path (co_awaited from io_task via as_awaitable) + // awaitable path (co_awaited from task> via as_awaitable) template struct awaitable : work_item { diff --git a/bench/bench.cpp b/bench/bench.cpp index b80624195..b9d42957a 100644 --- a/bench/bench.cpp +++ b/bench/bench.cpp @@ -8,7 +8,7 @@ // #include -#include +#include #include #include #include @@ -128,8 +128,8 @@ struct foreign_awaitable { } - // IoAwaitable protocol - std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const + // ContextAwaitable protocol + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const*) const { return h; } diff --git a/bench/stdexec/awaitable_sender.hpp b/bench/stdexec/awaitable_sender.hpp index aaadc14ba..61b1ae1cd 100644 --- a/bench/stdexec/awaitable_sender.hpp +++ b/bench/stdexec/awaitable_sender.hpp @@ -10,11 +10,11 @@ #ifndef BOOST_CAPY_BENCH_STDEXEC_AWAITABLE_SENDER_HPP #define BOOST_CAPY_BENCH_STDEXEC_AWAITABLE_SENDER_HPP -#include +#include #include +#include #include -#include -#include +#include #include @@ -31,7 +31,7 @@ namespace boost::capy { // Query CPO for obtaining a Capy-compatible executor // from a P2300 environment. The returned object must // satisfy Capy's Executor concept. Environments that -// host IoAwaitables via the as_sender_lossy bridge must +// host ContextAwaitables via the as_sender_lossy bridge must // answer this query. struct get_io_executor_t { @@ -120,24 +120,24 @@ auto resolve_executor(Env const& env) } // namespace detail -/** Sender that wraps an IoAwaitable. +/** Sender that wraps a ContextAwaitable. When connected or co_awaited, the bridge queries the receiver's or promise's environment for a Capy-compatible executor via get_io_executor. The executor is stored by value in the operation - state and used to construct the io_env passed to - the IoAwaitable's await_suspend. + state and used to construct the await_context passed to + the ContextAwaitable's await_suspend. - @tparam IoAw The IoAwaitable type. + @tparam CtxAw The ContextAwaitable type. */ -template +template struct awaitable_sender { using sender_concept = stdexec::sender_tag; using result_type = decltype( - std::declval&>().await_resume()); + std::declval&>().await_resume()); static auto make_sigs() { @@ -170,7 +170,7 @@ struct awaitable_sender using completion_signatures = decltype(make_sigs()); - IoAw aw_; + CtxAw aw_; template struct op_state @@ -187,13 +187,13 @@ struct awaitable_sender stdexec::get_env( std::declval()))); - IoAw aw_; + CtxAw aw_; Receiver rcvr_; executor_type ex_; - io_env env_; + await_context env_; detail::frame_cb cb_; - op_state(IoAw aw, Receiver rcvr) + op_state(CtxAw aw, Receiver rcvr) : aw_(std::move(aw)) , rcvr_(std::move(rcvr)) , ex_{} @@ -312,7 +312,7 @@ struct awaitable_sender stdexec::get_stop_token_t{}); } - env_ = io_env{ex_, st, nullptr}; + env_ = await_context{ex_, st, nullptr}; if(aw_.await_ready()) { @@ -352,7 +352,7 @@ struct awaitable_sender // Bypass stdexec's sender_awaitable when co_awaited // from a coroutine that provides get_io_executor or // a start scheduler with get_io_executor. Adapts the - // IoAwaitable's 2-arg await_suspend to the standard + // ContextAwaitable's 2-arg await_suspend to the standard // 1-arg protocol. template auto as_awaitable(Promise& promise) && @@ -373,10 +373,10 @@ struct awaitable_sender struct aw { - IoAw aw_; + CtxAw aw_; executor_type ex_; std::stop_token st_; - io_env env_; + await_context env_; bool await_ready() noexcept { @@ -386,7 +386,7 @@ struct awaitable_sender std::coroutine_handle<> await_suspend(std::coroutine_handle<> h) { - env_ = io_env{ex_, st_, nullptr}; + env_ = await_context{ex_, st_, nullptr}; return aw_.await_suspend(h, &env_); } @@ -400,7 +400,7 @@ struct awaitable_sender } }; -/** Create a stdexec sender from an IoAwaitable. +/** Create a stdexec sender from a ContextAwaitable. The bridge routes the awaitable's result through sender channels based on its type: @@ -421,21 +421,21 @@ struct awaitable_sender via get_io_executor. The environment must answer this query with an object satisfying Capy's Executor concept. - @param aw The IoAwaitable to wrap. + @param aw The ContextAwaitable to wrap. @return A sender whose completion channels reflect the awaitable's result type. */ // Benchmark-only bridge. Unlike the canonical as_sender in -// example/awaitable-sender, this one accepts a compound io_result and +// example/awaitable-sender, this one accepts a compound `results` and // splits it at runtime: set_value(n) on success, set_error(ec) on // failure, dropping the byte count that accompanied the error. The // benchmark keeps it to measure the bridge without the task // wrapper the canonical bridge requires; do not copy it into examples. -template -auto as_sender_lossy(IoAw&& aw) +template +auto as_sender_lossy(CtxAw&& aw) { - return awaitable_sender>{ - std::forward(aw)}; + return awaitable_sender>{ + std::forward(aw)}; } // split_ec: sender adapter that routes error_code to diff --git a/bench/stdexec/ctxaw_io_read_stream.hpp b/bench/stdexec/ctxaw_io_read_stream.hpp new file mode 100644 index 000000000..9062bc925 --- /dev/null +++ b/bench/stdexec/ctxaw_io_read_stream.hpp @@ -0,0 +1,36 @@ +// +// Copyright (c) 2026 Steve Gerbino +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/capy +// + +#ifndef BOOST_CAPY_BENCH_STDEXEC_CTXAW_IO_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_STDEXEC_CTXAW_IO_READ_STREAM_HPP + +#include "ctxaw_read_stream.hpp" + +/// Abstract interface for ContextAwaitable read streams. +struct ctxaw_io_read_stream +{ + virtual ctxaw_read_stream::read_awaitable + read_some(boost::capy::mutable_buffer) = 0; + virtual ~ctxaw_io_read_stream() = default; +}; + +/// Concrete implementation of ctxaw_io_read_stream wrapping +/// an ctxaw_read_stream. +struct ctxaw_io_read_stream_impl : ctxaw_io_read_stream +{ + ctxaw_read_stream stream_; + + ctxaw_read_stream::read_awaitable + read_some(boost::capy::mutable_buffer buf) override + { + return stream_.read_some(buf); + } +}; + +#endif diff --git a/bench/beman/ioaw_read_stream.hpp b/bench/stdexec/ctxaw_read_stream.hpp similarity index 72% rename from bench/beman/ioaw_read_stream.hpp rename to bench/stdexec/ctxaw_read_stream.hpp index 11f6ce30c..50071f7fa 100644 --- a/bench/beman/ioaw_read_stream.hpp +++ b/bench/stdexec/ctxaw_read_stream.hpp @@ -7,23 +7,23 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_BENCH_IOAW_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_IOAW_READ_STREAM_HPP +#ifndef BOOST_CAPY_BENCH_STDEXEC_CTXAW_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_STDEXEC_CTXAW_READ_STREAM_HPP #include #include #include -#include -#include +#include +#include #include #include /// No-op ReadStream for benchmarking. /// -/// Uses the executor from io_env (passed by capy::task's +/// Uses the executor from await_context (passed by capy::task's /// transform_awaiter) to post the coroutine back. Satisfies /// ReadStream so it can be wrapped by capy::any_read_stream. -struct ioaw_read_stream +struct ctxaw_read_stream { struct read_awaitable { @@ -34,14 +34,14 @@ struct ioaw_read_stream std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - boost::capy::io_env const* env) + boost::capy::await_context const* env) { cont_.h = h; env->executor.post(cont_); return std::noop_coroutine(); } - boost::capy::io_result + boost::capy::results await_resume() noexcept { return {std::error_code(), 0}; } }; @@ -52,6 +52,6 @@ struct ioaw_read_stream } }; -static_assert(boost::capy::ReadStream); +static_assert(boost::capy::ReadStream); #endif diff --git a/bench/stdexec/ioaw_sync_read_stream.hpp b/bench/stdexec/ctxaw_sync_read_stream.hpp similarity index 72% rename from bench/stdexec/ioaw_sync_read_stream.hpp rename to bench/stdexec/ctxaw_sync_read_stream.hpp index 84f2d16aa..0c7114e50 100644 --- a/bench/stdexec/ioaw_sync_read_stream.hpp +++ b/bench/stdexec/ctxaw_sync_read_stream.hpp @@ -8,7 +8,7 @@ // // -// Synchronous-completion IoAwaitable stream. +// Synchronous-completion ContextAwaitable stream. // // Every read completes immediately via symmetric // transfer — await_suspend returns the coroutine @@ -16,16 +16,16 @@ // round-trip. // -#ifndef BOOST_CAPY_BENCH_STDEXEC_IOAW_SYNC_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_STDEXEC_IOAW_SYNC_READ_STREAM_HPP +#ifndef BOOST_CAPY_BENCH_STDEXEC_CTXAW_SYNC_READ_STREAM_HPP +#define BOOST_CAPY_BENCH_STDEXEC_CTXAW_SYNC_READ_STREAM_HPP -#include -#include +#include +#include #include #include -struct ioaw_sync_read_stream +struct ctxaw_sync_read_stream { struct read_awaitable { @@ -37,13 +37,13 @@ struct ioaw_sync_read_stream std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - boost::capy::io_env const*) + boost::capy::await_context const*) { // Data already buffered — resume inline return h; } - boost::capy::io_result + boost::capy::results await_resume() noexcept { return {std::error_code(), 0}; diff --git a/bench/stdexec/ioaw_io_read_stream.hpp b/bench/stdexec/ioaw_io_read_stream.hpp deleted file mode 100644 index eb18dc9bf..000000000 --- a/bench/stdexec/ioaw_io_read_stream.hpp +++ /dev/null @@ -1,36 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/capy -// - -#ifndef BOOST_CAPY_BENCH_STDEXEC_IOAW_IO_READ_STREAM_HPP -#define BOOST_CAPY_BENCH_STDEXEC_IOAW_IO_READ_STREAM_HPP - -#include "ioaw_read_stream.hpp" - -/// Abstract interface for IoAwaitable read streams. -struct ioaw_io_read_stream -{ - virtual ioaw_read_stream::read_awaitable - read_some(boost::capy::mutable_buffer) = 0; - virtual ~ioaw_io_read_stream() = default; -}; - -/// Concrete implementation of ioaw_io_read_stream wrapping -/// an ioaw_read_stream. -struct ioaw_io_read_stream_impl : ioaw_io_read_stream -{ - ioaw_read_stream stream_; - - ioaw_read_stream::read_awaitable - read_some(boost::capy::mutable_buffer buf) override - { - return stream_.read_some(buf); - } -}; - -#endif diff --git a/bench/stdexec/main.cpp b/bench/stdexec/main.cpp index 8ad5e6685..92263cf7b 100644 --- a/bench/stdexec/main.cpp +++ b/bench/stdexec/main.cpp @@ -27,12 +27,12 @@ #include "allocation_tracker.hpp" #include "awaitable_sender.hpp" -#include "ioaw_io_read_stream.hpp" -#include "ioaw_read_stream.hpp" -#include "ioaw_sync_read_stream.hpp" +#include "ctxaw_io_read_stream.hpp" +#include "ctxaw_read_stream.hpp" +#include "ctxaw_sync_read_stream.hpp" #include #include "sender_awaitable.hpp" -#include "sender_io_env.hpp" +#include "sender_await_context.hpp" #include "sndr_any_read_stream.hpp" #include "sndr_io_read_stream.hpp" #include "sndr_read_stream.hpp" @@ -190,7 +190,7 @@ auto exec_accept(Stream& stream, cell_result& out) // ----------------------------------------------------------- template -auto exec_session_ioaw( +auto exec_session_ctxaw( Stream& stream, sender_as_capy_executor ex) -> exec::task { @@ -204,7 +204,7 @@ auto exec_session_ioaw( } template -auto exec_accept_ioaw( +auto exec_accept_ctxaw( Stream& stream, sender_as_capy_executor ex, cell_result& out) -> exec::task @@ -213,7 +213,7 @@ auto exec_accept_ioaw( auto start = std::chrono::steady_clock::now(); for (int i = 0; i < OUTER_LOOPS; ++i) - co_await exec_session_ioaw(stream, ex); + co_await exec_session_ctxaw(stream, ex); auto elapsed = std::chrono::steady_clock::now() - start; auto after = g_alloc_count.load(std::memory_order_relaxed); @@ -363,11 +363,11 @@ int main() // Col B: Awaitable (via as_sender_lossy bridge) - // Native - ioaw_read_stream + // Native - ctxaw_read_stream { exec::static_thread_pool pool(2); static_pool_context ctx; - ioaw_read_stream stream; + ctxaw_read_stream stream; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; int count = OPS_PER_CELL; @@ -392,11 +392,11 @@ int main() after - before}; } - // Abstract - ioaw_io_read_stream + // Abstract - ctxaw_io_read_stream { exec::static_thread_pool pool(2); static_pool_context ctx; - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; int count = OPS_PER_CELL; @@ -408,7 +408,7 @@ int main() stdexec::let_value(stdexec::just(), [&]() { return stdexec::write_env( capy::as_sender_lossy( - static_cast( + static_cast( stream).read_some( capy::mutable_buffer( buf, sizeof(buf)))), @@ -428,7 +428,7 @@ int main() { exec::static_thread_pool pool(2); static_pool_context ctx; - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; @@ -454,11 +454,11 @@ int main() after - before}; } - // Synchronous - ioaw_sync_read_stream (Col B) + // Synchronous - ctxaw_sync_read_stream (Col B) { exec::static_thread_pool pool(2); static_pool_context ctx; - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; int count = OPS_PER_CELL; @@ -489,22 +489,22 @@ int main() // Col A: Awaitable (native) - // Native - ioaw_read_stream + // Native - ctxaw_read_stream { capy::thread_pool pool(1); - ioaw_read_stream stream; + ctxaw_read_stream stream; capy::run_async(pool.get_executor())( capy_accept(stream, grid[run][CAPY_TASK][NATIVE_STREAM][NATIVE_EXEC_MODEL])); pool.join(); } - // Abstract - ioaw_io_read_stream + // Abstract - ctxaw_io_read_stream { capy::thread_pool pool(1); - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; capy::run_async(pool.get_executor())( - capy_accept(static_cast(stream), + capy_accept(static_cast(stream), grid[run][CAPY_TASK][ABSTRACT_STREAM][NATIVE_EXEC_MODEL])); pool.join(); } @@ -512,7 +512,7 @@ int main() // Type erased - capy::any_read_stream { capy::thread_pool pool(1); - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); capy::run_async(pool.get_executor())( capy_accept(stream, @@ -520,10 +520,10 @@ int main() pool.join(); } - // Synchronous - ioaw_sync_read_stream + // Synchronous - ctxaw_sync_read_stream { capy::thread_pool pool(1); - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; capy::run_async(pool.get_executor())( capy_accept(stream, grid[run][CAPY_TASK][SYNC_STREAM][NATIVE_EXEC_MODEL])); @@ -660,29 +660,29 @@ int main() // Col B: Awaitable (via as_sender_lossy bridge) - // Native - ioaw_read_stream + // Native - ctxaw_read_stream { exec::static_thread_pool pool(1); static_pool_context ctx; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; - ioaw_read_stream stream; + ctxaw_read_stream stream; stdexec::sync_wait(stdexec::starts_on(sched, - exec_accept_ioaw(stream, adapter, + exec_accept_ctxaw(stream, adapter, grid[run][EXEC_TASK][NATIVE_STREAM][BRIDGED_EXEC_MODEL]))); pool.request_stop(); } - // Abstract - ioaw_io_read_stream + // Abstract - ctxaw_io_read_stream { exec::static_thread_pool pool(1); static_pool_context ctx; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; - ioaw_io_read_stream_impl stream; + ctxaw_io_read_stream_impl stream; stdexec::sync_wait(stdexec::starts_on(sched, - exec_accept_ioaw( - static_cast(stream), + exec_accept_ctxaw( + static_cast(stream), adapter, grid[run][EXEC_TASK][ABSTRACT_STREAM][BRIDGED_EXEC_MODEL]))); pool.request_stop(); @@ -694,28 +694,28 @@ int main() static_pool_context ctx; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; - ioaw_read_stream concrete; + ctxaw_read_stream concrete; capy::any_read_stream stream(&concrete); auto* mr = get_counting_resource(); std::pmr::polymorphic_allocator alloc(mr); stdexec::sync_wait( stdexec::write_env( stdexec::starts_on(sched, - exec_accept_ioaw(stream, adapter, + exec_accept_ctxaw(stream, adapter, grid[run][EXEC_TASK][TYPE_ERASED_STREAM][BRIDGED_EXEC_MODEL])), stdexec::prop{exec::get_frame_allocator, alloc})); pool.request_stop(); } - // Synchronous - ioaw_sync_read_stream + // Synchronous - ctxaw_sync_read_stream { exec::static_thread_pool pool(1); static_pool_context ctx; pool_scheduler sched{&pool, &ctx}; sender_as_capy_executor adapter{&pool, &ctx}; - ioaw_sync_read_stream stream; + ctxaw_sync_read_stream stream; stdexec::sync_wait(stdexec::starts_on(sched, - exec_accept_ioaw(stream, adapter, + exec_accept_ctxaw(stream, adapter, grid[run][EXEC_TASK][SYNC_STREAM][BRIDGED_EXEC_MODEL]))); pool.request_stop(); } diff --git a/bench/stdexec/sender_io_env.hpp b/bench/stdexec/sender_await_context.hpp similarity index 97% rename from bench/stdexec/sender_io_env.hpp rename to bench/stdexec/sender_await_context.hpp index de238db4b..0aded0edb 100644 --- a/bench/stdexec/sender_io_env.hpp +++ b/bench/stdexec/sender_await_context.hpp @@ -14,8 +14,8 @@ // so capy::task can run on it. // -#ifndef BOOST_CAPY_BENCH_STDEXEC_SENDER_IO_ENV_HPP -#define BOOST_CAPY_BENCH_STDEXEC_SENDER_IO_ENV_HPP +#ifndef BOOST_CAPY_BENCH_STDEXEC_SENDER_AWAIT_CONTEXT_HPP +#define BOOST_CAPY_BENCH_STDEXEC_SENDER_AWAIT_CONTEXT_HPP #include "awaitable_sender.hpp" diff --git a/bench/stdexec/sender_awaitable.hpp b/bench/stdexec/sender_awaitable.hpp index 16c3bb9d8..b3c8ce436 100644 --- a/bench/stdexec/sender_awaitable.hpp +++ b/bench/stdexec/sender_awaitable.hpp @@ -11,8 +11,8 @@ #define BOOST_CAPY_BENCH_STDEXEC_SENDER_AWAITABLE_HPP #include -#include -#include +#include +#include #include @@ -184,7 +184,7 @@ struct bridge_receiver /** Awaitable that bridges a stdexec sender into a Capy coroutine. - Satisfies IoAwaitable. When co_awaited inside + Satisfies ContextAwaitable. When co_awaited inside a capy::task, connects the sender to a bridge receiver, starts the operation, and resumes the coroutine when the sender completes. @@ -199,7 +199,7 @@ struct bridge_receiver completion signatures at compile time. If the sender can complete with set_error(std::error_code), await_resume - returns io_result so the error code is a + returns results so the error code is a value, not an exception. Otherwise await_resume returns the value directly and genuine exceptions are rethrown. @@ -269,7 +269,7 @@ struct [[nodiscard]] sender_awaitable std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { ::new(op_buf_) op_state_type( stdexec::connect( @@ -303,7 +303,7 @@ struct [[nodiscard]] sender_awaitable private: // Sender can complete with - // set_error(error_code). Return io_result + // set_error(error_code). Return `results` // so the error code is a value, not an // exception. auto await_resume_ec() @@ -319,13 +319,13 @@ struct [[nodiscard]] sender_awaitable { // stopped at index 4 if(result_.index() == 4) - return io_result<>{ + return results<>{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result<>{ + return results<>{ std::get<2>(result_)}; - return io_result<>{}; + return results<>{}; } else if constexpr ( std::tuple_size_v< @@ -334,13 +334,13 @@ struct [[nodiscard]] sender_awaitable using T = std::tuple_element_t< 0, value_tuple>; if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled), T{}}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_), T{}}; - return io_result{ + return results{ std::error_code(), std::get<0>( std::get<1>( @@ -349,13 +349,13 @@ struct [[nodiscard]] sender_awaitable else { if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled), value_tuple{}}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_), value_tuple{}}; - return io_result{ + return results{ std::error_code(), std::get<1>( std::move(result_))}; @@ -391,11 +391,11 @@ struct [[nodiscard]] sender_awaitable } }; -/** Create an IoAwaitable from a stdexec sender. +/** Create a ContextAwaitable from a stdexec sender. If the sender can complete with set_error(std::error_code), the returned - awaitable yields io_result so the error code + awaitable yields results so the error code is a value, not an exception. Otherwise the awaitable yields the value directly. @@ -412,7 +412,7 @@ struct [[nodiscard]] sender_awaitable @endcode @param sndr The sender to bridge. - @return An IoAwaitable that can be co_awaited + @return A ContextAwaitable that can be co_awaited in a capy::task. */ template diff --git a/doc/.vale/styles/Capy/Terminology.yml b/doc/.vale/styles/Capy/Terminology.yml index 2cddf18eb..fe98daac5 100644 --- a/doc/.vale/styles/Capy/Terminology.yml +++ b/doc/.vale/styles/Capy/Terminology.yml @@ -13,7 +13,7 @@ # `launch`/`spawn`-bearing identifier in the library and the pages is either # snake_case (`launch_one`, `launch_all`, `spawn_work`, `co_spawn`, # `launch_policies`, the snippet tag `4b_launching`) or a `launcher` compound -# (`when_any_io_launcher`, `launchable`, `relaunch`, `launchers`). `_` is a word +# (`when_any_results_launcher`, `launchable`, `relaunch`, `launchers`). `_` is a word # character, so the `\b` after the stem already excludes the snake_case forms, # and the inflection list below is deliberately limited to VERB endings so it # cannot reach `launcher`. On top of that, `.vale.ini`'s `TokenIgnores` blanks diff --git a/doc/.vale/styles/config/vocabularies/Capy/accept.txt b/doc/.vale/styles/config/vocabularies/Capy/accept.txt index 8619f6fe6..eb0654b25 100644 --- a/doc/.vale/styles/config/vocabularies/Capy/accept.txt +++ b/doc/.vale/styles/config/vocabularies/Capy/accept.txt @@ -101,7 +101,7 @@ # --- C++ terms used adjectivally or nominally in running prose ------------------ # # Language keywords belong here, and several already did (const, nullptr, nothrow, -# enum, bool). The line against bare identifiers below is about SYMBOLS: `io_result` +# enum, bool). The line against bare identifiers below is about SYMBOLS: `results` # has a generated reference page, so leaving it unlinked in prose is a real missed # `cpp:` link (style guide B1). A keyword has no page and nothing to link to, so B1 # offers no remedy and the alert can never be actioned -- it is noise, not a signal. diff --git a/doc/STYLE_GUIDE.md b/doc/STYLE_GUIDE.md index 994da5c29..b85e4a90a 100644 --- a/doc/STYLE_GUIDE.md +++ b/doc/STYLE_GUIDE.md @@ -154,7 +154,7 @@ as vocabulary is added — extend it rather than letting synonyms drift.)* | callback passed to `run_async` | **completion handler** | handler (bare), callback | | a `task` value | **task** | coroutine (the language feature), coro | | the C++20 language feature | **coroutine** | coro, async function | -| awaitable satisfying `IoAwaitable` | **I/O awaitable** | awaitable (bare, when the concept is meant) | +| awaitable satisfying `ContextAwaitable` | **I/O awaitable** | awaitable (bare, when the concept is meant) | Approved technical names (need no paraphrase): coroutine, task, promise, awaiter, awaitable, executor, execution context, strand, thread pool, allocator, frame, buffer, diff --git a/doc/as_sender.md b/doc/as_sender.md index d5e4be239..8f921aa0e 100644 --- a/doc/as_sender.md +++ b/doc/as_sender.md @@ -1,23 +1,23 @@ # Zero-Allocation Sender-to-Awaitable Bridge -Every IoAwaitable ever written - timers, mutexes, channels, semaphores, +Every ContextAwaitable ever written - timers, mutexes, channels, semaphores, file I/O, sockets, database queries, HTTP clients - is now consumable by a sender pipeline. No coroutine frame. No heap allocation. Zero cost. ## What This Is -`as_sender` wraps any IoAwaitable in a P2300-compliant sender. A +`as_sender` wraps any ContextAwaitable in a P2300-compliant sender. A receiver attaches to the sender through `connect`. When `start` is called, the operation state drives the awaitable protocol directly - `await_ready`, `await_suspend`, `await_resume` - without ever creating a coroutine. The awaitable does not know it is talking to a sender. It sees a -`coroutine_handle<>` and an `io_env*`, exactly as it would from a +`coroutine_handle<>` and an `await_context*`, exactly as it would from a coroutine. The awaitable's code does not change. Not one line. ```cpp -// Wrap any IoAwaitable as a sender +// Wrap any ContextAwaitable as a sender auto sndr = as_sender(stream.read_some(buf)); // Attach a receiver and start the operation @@ -71,13 +71,13 @@ Here is what happens, step by step: Nothing runs yet. Senders are lazy. - **`connect(sender, receiver)`** produces an operation state. The - operation state holds the awaitable, the receiver, an `io_env`, and + operation state holds the awaitable, the receiver, an `await_context`, and a `frame_cb`. Everything lives on the operation state. No allocation. - **`start(op_state)`** begins the operation: 1. The executor and stop token are pulled from the receiver's - environment and stored in the `io_env`. + environment and stored in the `await_context`. 2. `await_ready()` is checked. If the awaitable is immediately ready, the result is harvested and the receiver is signaled @@ -109,14 +109,14 @@ without a coroutine ever existing. The awaitable ecosystem and the sender ecosystem are no longer separate worlds. They are one world. -Every IoAwaitable anyone has written becomes a sender with a single +Every ContextAwaitable anyone has written becomes a sender with a single function call. Awaitable authors gain a new consumer base without modifying a single line of their code. Sender authors gain access to every I/O primitive the awaitable ecosystem has produced - and will produce - at zero allocation cost. - **One I/O implementation.** The library implements each operation - once as an IoAwaitable. Coroutines `co_await` it. Sender pipelines + once as a ContextAwaitable. Coroutines `co_await` it. Sender pipelines consume it through `as_sender`. Both go through the same reactor, the same executor, the same platform code. @@ -128,11 +128,11 @@ produce - at zero allocation cost. - **Full protocol fidelity.** The bridge respects `await_ready` for synchronous fast-paths. It normalizes `await_suspend` return types (`void`, `bool`, `coroutine_handle<>`). It propagates the executor - and stop token through `io_env`. It routes results to `set_value`, + and stop token through `await_context`. It routes results to `set_value`, errors to `set_error`, and cancellation to `set_stopped`. - **Transparent to the awaitable.** The awaitable sees a - `coroutine_handle<>` and an `io_env const*`. It does not know + `coroutine_handle<>` and an `await_context const*`. It does not know whether the handle points at a coroutine frame or a `frame_cb`. It does not need to know. The handle is the abstraction boundary, and the abstraction holds. @@ -148,7 +148,7 @@ produce - at zero allocation cost. namespace capy = boost::capy; namespace ex = beman::execution; -// A Capy IoAwaitable - a single-slot waker wait +// A Capy ContextAwaitable - a single-slot waker wait capy::async_waker waker; auto sndr = capy::as_sender(waker.wait()); @@ -167,7 +167,7 @@ waker.wake(); ``` The receiver's environment provides the executor and stop token. The -bridge threads them into the `io_env` that the awaitable expects. When +bridge threads them into the `await_context` that the awaitable expects. When `waker.wake()` runs, the executor resumes the handle, the receiver gets `set_value()`. Twenty-four bytes of `frame_cb` on the operation state. That is the entire cost. diff --git a/doc/buffers-passing-rationale.md b/doc/buffers-passing-rationale.md index 5f172f202..d41c98d36 100644 --- a/doc/buffers-passing-rationale.md +++ b/doc/buffers-passing-rationale.md @@ -9,7 +9,7 @@ implementations copy the buffer sequence, or whether they should accept by reference and leave lifetime management to the caller. A secondary question is whether the distinction between coroutine-based -and non-coroutine-based implementations (tasks returning `io_task` vs. +and non-coroutine-based implementations (tasks returning `task>` vs. raw awaitables) changes the answer. The discussion took place between Vinnie Falco and Peter Dimov. The @@ -27,12 +27,12 @@ The capy documentation currently states: This statement is acknowledged to be backwards. The discussion produced the following corrected understanding: -- A function that returns an `IoAwaitable` directly (a raw awaitable, +- A function that returns a `ContextAwaitable` directly (a raw awaitable, not backed by a coroutine frame) must store the buffer sequence inside the awaitable, because there is no coroutine frame to hold it. Taking by value ensures the sequence lives in the returned object. -- A function that returns an `io_task` (a coroutine) type-erases the +- A function that returns a `task>` (a coroutine) type-erases the buffer sequence into its coroutine frame. In this case the caller's object is referenced across suspension points through the coroutine frame itself, and taking by `const&` is correct - the sequence is not @@ -158,7 +158,7 @@ template class xoring_stream ReadStream& s_; public: template - io_task read_some(Buffers buffers) // first copy here + task> read_some(Buffers buffers) // first copy here { auto [ec, n] = co_await s_.read_some(buffers); // second copy here xor_buffers(buffers, n); @@ -219,11 +219,11 @@ which makes by-`&&` equivalent to by-value for the storage decision. The passing convention differs by return type: -- `io_task read_some(Buffers const& buf)` - the coroutine frame +- `task> read_some(Buffers const& buf)` - the coroutine frame is the storage. The caller's object is kept alive by the `co_await` chain. By-`const&` is correct and no copy is made. -- `IoAwaitable auto read_some(Buffers buf)` - the returned object is the +- `ContextAwaitable auto read_some(Buffers buf)` - the returned object is the storage. The awaitable must be self-contained. By-value is required. The concept as written applies to both cases with a single signature, @@ -233,7 +233,7 @@ cost of a more complex concept. ### The Array Problem -The `io_task` path accepts `const_buffer buf[N]` today because it takes +The `task>` path accepts `const_buffer buf[N]` today because it takes by `const&` and does not make a copy. If the concept is tightened to require a copy, raw arrays are excluded. The fix is `std::array`, but this is a source-compatibility break for any code that passes C arrays @@ -253,13 +253,13 @@ today. `std::array` or a container. The concept should not be weakened to accommodate `T[]`. -4. **Raw awaitables must store a copy.** A function returning an - `IoAwaitable` directly, without a backing coroutine frame, must +4. **Raw awaitables must store a copy.** A function returning a + `ContextAwaitable` directly, without a backing coroutine frame, must store the buffer sequence in the returned object. By-value is the only safe signature. 5. **Coroutine-based tasks can accept by `const&`.** When the function - is a coroutine returning `io_task`, the sequence lives in the + is a coroutine returning `task>`, the sequence lives in the caller's frame across the `co_await`. No copy is needed by the callee. diff --git a/doc/buffers-peter.md b/doc/buffers-peter.md index 0f333f9b2..41ce6c8a0 100644 --- a/doc/buffers-peter.md +++ b/doc/buffers-peter.md @@ -75,7 +75,7 @@ This doesn't increase the total implementation complexity compared to the user p At the moment, read_some is specified as template -IoAwaitable auto read_some(Buffers buffers); +ContextAwaitable auto read_some(Buffers buffers); That is, it takes buffers by value. This doesn't seem correct. It would imply that std::vector is required to be copied by read_some, which is unnecessary. diff --git a/doc/combinator-gap-analysis.md b/doc/combinator-gap-analysis.md index 4a3a0910f..cb0a663f9 100644 --- a/doc/combinator-gap-analysis.md +++ b/doc/combinator-gap-analysis.md @@ -11,7 +11,7 @@ This document compares the combinator capabilities of two models: - **Coroutine-native** (`io::when_all`, `io::when_any`) as specified in `combinators.md` - **Sender/receiver** (`std::execution::when_all`) as specified in P2300R10 -The comparison assumes the sender model routes I/O results correctly: `!ec` goes to `set_value(T...)`, `ec` goes to `set_error(ec)`. This routing happens in the coroutine body - the translation layer described in P4093R0 (Falco, Gerbino). The coroutine absorbs the compound `io_result{ec, n}`, uses `n` on the success path, and propagates `ec` on the failure path. By the time the result crosses the sender boundary, it is clean: value or error, never both. +The comparison assumes the sender model routes I/O results correctly: `!ec` goes to `set_value(T...)`, `ec` goes to `set_error(ec)`. This routing happens in the coroutine body - the translation layer described in P4093R0 (Falco, Gerbino). The coroutine absorbs the compound `results{ec, n}`, uses `n` on the success path, and propagates `ec` on the failure path. By the time the result crosses the sender boundary, it is clean: value or error, never both. --- @@ -43,7 +43,7 @@ Assuming correct routing (`!ec` -> `set_value`, `ec` -> `set_error`): | # | Scenario | Coroutine-native | Sender | Gap? | |---|----------|------------------|--------|------| -| 1 | All tasks succeed | `io_result` with `!ec` | `tuple` via `set_value` | Ergonomic only | +| 1 | All tasks succeed | `results` with `!ec` | `tuple` via `set_value` | Ergonomic only | | 2 | One task returns `ec` | Inspects `ec`. Cancels siblings. | `set_error(tuple(ec, T...))`. Cancels siblings. | None | | 3 | Multiple `ec` concurrently | First `ec` wins. | First `set_error` wins. | None | | 4 | EOF, `n == 0` | Error. Cancel. | `set_error(tuple(eof, 0))`. Cancel. | None | @@ -55,10 +55,10 @@ Assuming correct routing (`!ec` -> `set_value`, `ec` -> `set_error`): | 10 | Exception vs. `ec` | Exception wins. | Both are `set_error`. Distinguishable by type. | Minor (type dispatch needed) | | 11 | Parent stop | ECANCELED, standard behavior. | `set_stopped`. Standard behavior. | None | | 12 | All tasks fail | First `ec` wins. | First `set_error` wins. | None | -| 13 | Failure reporting | Outer `io_result`'s `ec`. | `set_error` channel. | Ergonomic only | -| 14 | Return type | `io_result`. One `ec`, flat destructuring. | `tuple`. No `ec` lifting. | Ergonomic only | +| 13 | Failure reporting | Outer `results`'s `ec`. | `set_error` channel. | Ergonomic only | +| 14 | Return type | `results`. One `ec`, flat destructuring. | `tuple`. No `ec` lifting. | Ergonomic only | -**No structural gaps.** The sender `when_all` can do everything `io::when_all` can do when the routing is correct. With `set_error(tuple(ec, T...))`, partial bytes are preserved in the error payload. The differences are ergonomic: the coroutine-native return type (`io_result`) is more convenient than `tuple`. Neither difference affects capability. +**No structural gaps.** The sender `when_all` can do everything `io::when_all` can do when the routing is correct. With `set_error(tuple(ec, T...))`, partial bytes are preserved in the error payload. The differences are ergonomic: the coroutine-native return type (`results`) is more convenient than `tuple`. Neither difference affects capability. --- @@ -88,7 +88,7 @@ P2300 does not define `when_any`. A custom sender `when_any` can be written. Ass The key insight is that the compound result never needs to cross the sender boundary intact. The coroutine body decomposes it: ``` -I/O primitive -> io_result{ec, n} +I/O primitive -> results{ec, n} | coroutine body (translation layer) | @@ -109,7 +109,7 @@ The gaps are ergonomic, not structural: ### Return type convenience -Coroutine-native `io::when_all` returns `io_result` - one `ec`, flat destructuring: +Coroutine-native `io::when_all` returns `results` - one `ec`, flat destructuring: ```cpp auto [ec, n1, n2, n3] = co_await io::when_all( @@ -155,6 +155,6 @@ These are the sender model's strengths. They are orthogonal to I/O combinators. There are no structural gaps between the two models for I/O combinators. The three-channel model works for I/O when the compound result is decomposed in the coroutine body before crossing the sender boundary. This decomposition is the translation layer described in P4093R0. -The differences are ergonomic. The coroutine-native model provides a more convenient return type (`io_result` with one `ec` and flat destructuring), preserves partial bytes on the failure path, and ships `when_any` out of the box. The sender model requires the user to write `when_any` and returns less convenient types, but can express the same semantics. +The differences are ergonomic. The coroutine-native model provides a more convenient return type (`results` with one `ec` and flat destructuring), preserves partial bytes on the failure path, and ships `when_any` out of the box. The sender model requires the user to write `when_any` and returns less convenient types, but can express the same semantics. Both models require a coroutine body to decompose the compound result. The coroutine body is the bridge between I/O's compound results and any combinator model - whether channel-based or value-based. The question is not which model handles compound results better at the combinator level. Both handle them identically, because both rely on the same coroutine body to decompose them first. diff --git a/doc/combinator-spec.md b/doc/combinator-spec.md index ebc184f57..e206ef494 100644 --- a/doc/combinator-spec.md +++ b/doc/combinator-spec.md @@ -36,30 +36,30 @@ If any of `f1`...`fN` fails, you get the error - not a tuple of mixed results. T | 10 | One throws, another returns `ec` (either order) | Exception always wins. No value to return through an exception. Tuple is not accessible. | Settled | | 11 | Parent stop token fires | Not a special case. Children return ECANCELED, which is an error like any other. First ec wins, standard error behavior. | Settled | | 12 | All tasks fail | Propagate single error_code (first wins). Not a tuple of failures. | Settled | -| 13 | How does failure reach the caller? | Via the outer `io_result`'s `ec`. See Return Type below. | Settled | -| 14 | Return type | `io_result` where each `Ri` is `Ti` if child has single `T`, or `tuple` if child has multiple `T`s. See Return Type below. | Settled | +| 13 | How does failure reach the caller? | Via the outer `results`'s `ec`. See Return Type below. | Settled | +| 14 | Return type | `results` where each `Ri` is `Ti` if child has single `T`, or `tuple` if child has multiple `T`s. See Return Type below. | Settled | --- ## io::when_all Return Type -Peter Dimov's proposal: the return type lifts the error_code out of each child's `io_result` into a single outer `io_result`. Child value types are collected as parameters. +Peter Dimov's proposal: the return type lifts the error_code out of each child's `results` into a single outer `results`. Child value types are collected as parameters. -Given children returning `io_result`, `io_result`, ..., `io_result`: +Given children returning `results`, `results`, ..., `results`: ``` io::when_all(child1, child2, ..., childN) - -> io_result + -> results ``` Where each `Ri` is: -- `Ti` directly, if the child returns `io_result` (single type - no wrapping) -- `tuple` if the child returns `io_result` (multiple types) -- `tuple<>` if the child returns `io_result<>` (no value types) +- `Ti` directly, if the child returns `results` (single type - no wrapping) +- `tuple` if the child returns `results` (multiple types) +- `tuple<>` if the child returns `results<>` (no value types) ### Examples -Three reads, each returning `io_result`: +Three reads, each returning `results`: ```cpp auto [ec, n1, n2, n3] = co_await io::when_all( @@ -73,9 +73,9 @@ auto [ec, n1, n2, n3] = co_await io::when_all( Mixed result types: ```cpp -// task_a returns io_result -// task_b returns io_result -// task_c returns io_result +// task_a returns results +// task_b returns results +// task_c returns results auto [ec, n, tf, msg] = co_await io::when_all( task_a, task_b, task_c); // ec: single error_code @@ -84,12 +84,12 @@ auto [ec, n, tf, msg] = co_await io::when_all( // msg: message from task_c ``` -Void results (`io_result<>`): +Void results (`results<>`): ```cpp -// task_a returns io_result -// task_b returns io_result<> (just error_code, no values) -// task_c returns io_result +// task_a returns results +// task_b returns results<> (just error_code, no values) +// task_c returns results auto [ec, n1, empty, n2] = co_await io::when_all( task_a, task_b, task_c); // ec: single error_code @@ -98,14 +98,14 @@ auto [ec, n1, empty, n2] = co_await io::when_all( // n2: size_t from task_c ``` -`io_result<>` contributes a `tuple<>` to the outer parameter list. Same rule as multiple types (`tuple`), applied to zero types. +`results<>` contributes a `tuple<>` to the outer parameter list. Same rule as multiple types (`tuple`), applied to zero types. ### Why this works - **Binary outcome.** Caller checks one `ec`. On success, destructures the values. On failure, handles one error. Matches the synchronous analogy. - **No redundant error codes.** Child error codes are stripped. On the success path they were all zero. On the failure path only the first matters. - **Natural destructuring.** Single-type children flatten into the parameter list. `auto [ec, n1, n2, n3]` just works. -- **Consistent with io_result convention.** The result is itself an `io_result`, so it composes with anything that already handles `io_result`. +- **Consistent with `results` convention.** The result is itself a `results`, so it composes with anything that already handles `results`. --- @@ -138,15 +138,15 @@ io::when_any(child1, child2, ..., childN) ``` Where each `Ri` follows the same rules as `when_all`: -- `Ti` directly, if the child returns `io_result` (single type) -- `tuple` if the child returns `io_result` (multiple types) -- `tuple<>` if the child returns `io_result<>` (no value types) +- `Ti` directly, if the child returns `results` (single type) +- `tuple` if the child returns `results` (multiple types) +- `tuple<>` if the child returns `results<>` (no value types) Index 0 (`error_code`) is the failure/no-winner case. Index 1..N identifies which child won. ### Examples -Three reads, each returning `io_result`: +Three reads, each returning `results`: ```cpp auto result = co_await io::when_any( @@ -164,8 +164,8 @@ else Mixed result types: ```cpp -// task_a returns io_result -// task_b returns io_result +// task_a returns results +// task_b returns results auto result = co_await io::when_any(task_a, task_b); // result: variant @@ -182,7 +182,7 @@ else - **Winner identification.** `result.index() - 1` maps directly to the child index. No separate index field needed. - **Error at index 0.** Failure is always `index() == 0`. Simple boolean check: `if (result.index() == 0)`. - **No redundant error codes.** Winners have `!ec` by definition. The child's error_code is stripped. Only the failure case carries an error_code. -- **Consistent with when_all.** Same `Ri` flattening rules. Same error_code stripping. Different container (variant vs io_result). +- **Consistent with when_all.** Same `Ri` flattening rules. Same error_code stripping. Different container (variant vs `results`). --- @@ -194,7 +194,7 @@ All rows settled. Both tables complete. No open questions remain. ## Contributors -- Peter Dimov: primitive normalization rule, binary success/failure model, "first ec wins" semantics, outer io_result return type proposal, corrections to rows 5/10/12 +- Peter Dimov: primitive normalization rule, binary success/failure model, "first ec wins" semantics, outer `results` return type proposal, corrections to rows 5/10/12 - Andrzej Krzemienski: error_code is status not error, three-bucket classification (success/failure/cancellation) - Michael Vandeberg: predicate-based combinator design (superseded by concrete io:: approach), when_any winner selection, issues 204-206 - Vinnie Falco: ssl_stream_truncated counterexample, partial transfer preservation question diff --git a/doc/continuation-rationale.md b/doc/continuation-rationale.md index 0ea4a0a50..84c893943 100644 --- a/doc/continuation-rationale.md +++ b/doc/continuation-rationale.md @@ -8,7 +8,7 @@ capy's executor interface. The central question is whether the executor concept should traffic in raw coroutine handles or in a richer type that carries intrusive queue metadata. Secondary questions address where the `continuation` object lives, how it is passed, and what this means for -the promise base, the `IoAwaitable` protocol, and downstream consumers +the promise base, the `ContextAwaitable` protocol, and downstream consumers like corosio. The consensus was reached through discussion and prototyping. The @@ -35,8 +35,8 @@ concept Executor = requires(E& e, continuation c) { Both fields are public. The `continuation` lives in the I/O awaitable for caller-handle posting, and in combinator/trampoline state for -parent-dispatch and child-launch patterns. The `IoAwaitable` concept -is unchanged. The promise base (`io_awaitable_promise_base`) is +parent-dispatch and child-launch patterns. The `ContextAwaitable` concept +is unchanged. The promise base (`context_awaitable_promise_base`) is unchanged. The `dispatch` return type remains `std::coroutine_handle<>` for symmetric transfer. @@ -174,7 +174,7 @@ executor's queue. Two locations were considered: ### Option L1: In the Promise -`io_awaitable_promise_base` gains a `continuation` member. One +`context_awaitable_promise_base` gains a `continuation` member. One `continuation` per coroutine, reused across all suspension points. **Arguments for:** @@ -188,12 +188,12 @@ executor's queue. Two locations were considered: **Arguments against:** -1. Changes the `IoAwaitable` concept. `await_suspend` must receive +1. Changes the `ContextAwaitable` concept. `await_suspend` must receive `continuation&` instead of `coroutine_handle<>`, or the awaitable must reach into the caller's promise to get the continuation. Both are protocol changes. 2. Burdens task authors. Every promise type that inherits from - `io_awaitable_promise_base` grows by a pointer (the `reserved` + `context_awaitable_promise_base` grows by a pointer (the `reserved` field) even though most suspension points never queue the continuation (they use symmetric transfer inline). 3. Conflates two concerns. The promise stores "who resumes me when @@ -214,7 +214,7 @@ wraps it in the embedded `continuation`, and passes that to **Arguments for:** -1. No change to the `IoAwaitable` concept. The `continuation` is +1. No change to the `ContextAwaitable` concept. The `continuation` is an implementation detail of the awaitable, not a protocol concern. 2. The awaitable has a stable address for the duration of the suspension (the compiler guarantees this for the operand of @@ -240,19 +240,19 @@ wraps it in the embedded `continuation`, and passes that to | Property | Promise (L1) | Awaitable (L2) | |---|---|---| -| Changes `IoAwaitable` concept? | Yes | No | +| Changes `ContextAwaitable` concept? | Yes | No | | Continuations per coroutine | One, reused | One per `co_await` | | Init cost per suspension | None (already set) | Set `h` and `reserved` | | Alignment with corosio `scheduler_op` | Separate patterns | Same pattern | | Burden on task authors | Yes — inherits extra pointer | None | | Combinator / trampoline sites | Free (in promise) | Need explicit storage | -| `io_awaitable_promise_base` size | +8 bytes per coroutine | Unchanged | +| `context_awaitable_promise_base` size | +8 bytes per coroutine | Unchanged | **Recommendation:** Option L2. The `continuation` is about how an I/O operation interacts with the executor's queue — that is the awaitable's concern. The handful of combinator and trampoline sites that need their own `continuation` storage are internal to the library -and explicitly annotated. The promise base stays lean, the IoAwaitable +and explicitly annotated. The promise base stays lean, the ContextAwaitable protocol is untouched, and task authors see no change. ## The Dispatch Return Type Question @@ -400,7 +400,7 @@ only the input signature is the minimal, safe approach. ## The Promise Base Question -`io_awaitable_promise_base` stores the parent's coroutine handle via +`context_awaitable_promise_base` stores the parent's coroutine handle via `set_continuation(coroutine_handle<>)` / `continuation()`. Should this internal storage change from `coroutine_handle<>` to the `continuation` struct? @@ -490,7 +490,7 @@ The `continuation` change requires updates in corosio: `coroutine_handle<>`.** The allocation cost and safety risk are both real. -2. **The `IoAwaitable` concept should not change.** Awaitables +2. **The `ContextAwaitable` concept should not change.** Awaitables receive `coroutine_handle<>` in `await_suspend` and manage the `continuation` internally. @@ -512,7 +512,7 @@ The `continuation` change requires updates in corosio: | Per-post allocation (thread_pool) | `new work(h)` every call | None (intrusive queue) | | Per-post allocation (strand) | `strand_op` wrapper (recycled) | Same (wrapper retained) | | Type safety | Raw handle, easy to misuse | Struct, harder to fabricate | -| `IoAwaitable` concept | `await_suspend(handle, env)` | Unchanged | +| `ContextAwaitable` concept | `await_suspend(handle, env)` | Unchanged | | Promise base | `coroutine_handle<>` | Unchanged | | Combinator state | `coroutine_handle<>` fields | `continuation` fields | | Symmetric transfer | `dispatch` returns handle | Same | diff --git a/doc/executor-transfer.md b/doc/executor-transfer.md index 5ca69292a..564319208 100644 --- a/doc/executor-transfer.md +++ b/doc/executor-transfer.md @@ -239,14 +239,14 @@ If neither the caller nor the target is a strand, both `transfer_to` calls forwa The `transfer_to` design requires every launch function author to call `transfer_to` on both trips. If someone writes a custom `run`-like function and forgets the return trip, they reintroduce the bug. An RAII guard in the coroutine frame could make the fix automatic. Three approaches: -### Option A: Flag in io_env +### Option A: Flag in await_context -The strand sets a flag in the `io_env` when it dispatches a coroutine. The trampoline checks the flag on both trips and posts instead of dispatching when it is set. +The strand sets a flag in the `await_context` when it dispatches a coroutine. The trampoline checks the flag on both trips and posts instead of dispatching when it is set. -`io_env` gains a bool: +`await_context` gains a bool: ```cpp -struct io_env +struct await_context { executor_ref executor; std::stop_token stop_token; diff --git a/doc/io-benchmark-analysis.md b/doc/io-benchmark-analysis.md index 018331591..c830537f3 100644 --- a/doc/io-benchmark-analysis.md +++ b/doc/io-benchmark-analysis.md @@ -7,7 +7,7 @@ This benchmark compares three execution models for asynchronous I/O across three ## Trade-off summary -| Property | capy IoAwaitable | P2300 sender/receiver | +| Property | capy ContextAwaitable | P2300 sender/receiver | |-----------------------------------|---------------------------------------|----------------------------------------------| | Native concrete performance | ~31 ns/op, 0 al/op | ~32–34 ns/op, 0 al/op | | Type erasure cost (with recycler) | +5 ns/op, 0 al/op | +21–23 ns/op, 1 al/op (conditional on SBO fit [18]) | @@ -27,7 +27,7 @@ This benchmark compares three execution models for asynchronous I/O across three ## Results -All values are mean +/- stddev over 5 runs (warmup excluded). **Bold** = native execution model (Column A). al/op counts allocation calls per operation, including recycled allocations. The bridge column (B) in Tables 1 and 3 shows 1 al/op — the `scheduled_resume` operation state when IoAwaitables post through `schedule()` → `connect()` → `start()`. +All values are mean +/- stddev over 5 runs (warmup excluded). **Bold** = native execution model (Column A). al/op counts allocation calls per operation, including recycled allocations. The bridge column (B) in Tables 1 and 3 shows 1 al/op — the `scheduled_resume` operation state when ContextAwaitables post through `schedule()` → `connect()` → `start()`. ### Table 1: sender/receiver pipeline @@ -85,15 +85,15 @@ The allocation counts (from the native Column A): | Abstract | 1 | 0 | 1 | | Type-erased | 1 | 0 | 1 | -The IoAwaitable column (capy::task) shows 0 al/op at all abstraction levels. The sender columns show 1 al/op once the stream is abstracted — the type-erased `concrete_op` allocation that the recycler serves from its free list. +The ContextAwaitable column (capy::task) shows 0 al/op at all abstraction levels. The sender columns show 1 al/op once the stream is abstracted — the type-erased `concrete_op` allocation that the recycler serves from its free list. ### Bridges are competitive The non-bold column in each table measures the cost of consuming the opposite I/O type through a bridge. Both bridges use universally correct protocols — not optimized for this benchmark's specific senders. -- **await_sender** (sender → IoAwaitable, Table 2 Col B): Adds ~17 ns and **zero allocations** for native senders. The bridge connects the sender to a bridge receiver and uses an atomic exchange protocol to handle synchronous and asynchronous completion uniformly. The receiver resumes the coroutine directly — no posting through the executor. Abstract and type-erased senders show 1 al/op — the type-erased `concrete_op` allocation from the sender side, not the bridge. +- **await_sender** (sender → ContextAwaitable, Table 2 Col B): Adds ~17 ns and **zero allocations** for native senders. The bridge connects the sender to a bridge receiver and uses an atomic exchange protocol to handle synchronous and asynchronous completion uniformly. The receiver resumes the coroutine directly — no posting through the executor. Abstract and type-erased senders show 1 al/op — the type-erased `concrete_op` allocation from the sender side, not the bridge. -- **as_sender** (IoAwaitable → sender, Tables 1 and 3 Col B): For `beman::execution::task` (Table 3), the `awaitable_sender`'s `as_awaitable` member lets beman's `await_transform` [2, §33.9.11.8] call the IoAwaitable directly, bypassing beman's `sender_awaitable` wrapping. The overhead is ~11 ns over the native sender path. For the sender pipeline (Table 1), the bridge constructs a synthetic coroutine frame (`frame_cb`). Both paths incur 1 al/op from the `scheduled_resume` operation state — the P2300-mandated `schedule()` → `connect()` → `start()` protocol to resume a coroutine on the scheduler. +- **as_sender** (ContextAwaitable → sender, Tables 1 and 3 Col B): For `beman::execution::task` (Table 3), the `awaitable_sender`'s `as_awaitable` member lets beman's `await_transform` [2, §33.9.11.8] call the ContextAwaitable directly, bypassing beman's `sender_awaitable` wrapping. The overhead is ~11 ns over the native sender path. For the sender pipeline (Table 1), the bridge constructs a synthetic coroutine frame (`frame_cb`). Both paths incur 1 al/op from the `scheduled_resume` operation state — the P2300-mandated `schedule()` → `connect()` → `start()` protocol to resume a coroutine on the scheduler. ### The bridged awaitable outperforms native senders under abstraction @@ -102,7 +102,7 @@ In Table 3 (`beman::execution::task`), the bridged awaitable column (Col B) is * - Table 3 abstract: awaitable bridge 43.4 ns (1 al/op) vs sender native **55.2 ns (1 al/op)** - Table 3 type-erased: awaitable bridge 48.7 ns (1 al/op) vs sender native **55.2 ns (1 al/op)** -Both sides now show 1 al/op at the abstract/type-erased level, but the awaitable bridge is still 7–12 ns faster. The bridged awaitable's performance is remarkably flat across abstraction levels (43.5/43.4/48.7 ns), while the native sender jumps sharply from 31.9 ns (native) to 55.2 ns (abstract). This occurs because the bridge cost is constant — the IoAwaitable's `await_suspend` always follows the same path regardless of stream abstraction — while the sender model's virtual dispatch and type erasure machinery scale with abstraction level. +Both sides now show 1 al/op at the abstract/type-erased level, but the awaitable bridge is still 7–12 ns faster. The bridged awaitable's performance is remarkably flat across abstraction levels (43.5/43.4/48.7 ns), while the native sender jumps sharply from 31.9 ns (native) to 55.2 ns (abstract). This occurs because the bridge cost is constant — the ContextAwaitable's `await_suspend` always follows the same path regardless of stream abstraction — while the sender model's virtual dispatch and type erasure machinery scale with abstraction level. In Table 1 (sender pipeline), the bridge is slightly faster: bridge 54.1 ns vs native 57.5 ns at the type-erased level — both at 1 al/op. @@ -133,7 +133,7 @@ This mechanism recovers symmetric transfer **only when a sender is `co_await`'d ### Table 3 and `as_awaitable` -Table 3's native sender column (Col A) benefits from `bex::task`'s `as_awaitable` dispatch. When a sender provides an `as_awaitable` member, `bex::task`'s `await_transform` calls it directly — the sender's `connect` and `start` methods are never invoked. stdexec's implementation confirms this dispatch priority: the `as_awaitable` CPO uses a `__first_callable` chain that checks the sender's member function first, before falling back to generic wrapping [15]. The benchmark's `sndr_read_stream::read_sender` provides exactly this: an `as_awaitable` that returns a lightweight `work_item` awaitable, identical in cost to the IoAwaitable path. +Table 3's native sender column (Col A) benefits from `bex::task`'s `as_awaitable` dispatch. When a sender provides an `as_awaitable` member, `bex::task`'s `await_transform` calls it directly — the sender's `connect` and `start` methods are never invoked. stdexec's implementation confirms this dispatch priority: the `as_awaitable` CPO uses a `__first_callable` chain that checks the sender's member function first, before falling back to generic wrapping [15]. The benchmark's `sndr_read_stream::read_sender` provides exactly this: an `as_awaitable` that returns a lightweight `work_item` awaitable, identical in cost to the ContextAwaitable path. This explains why Table 3 native sender (31.9 ns/op) matches Table 2 native awaitable (31.4 ns/op) — both are measuring the awaitable path, not the sender protocol. @@ -143,7 +143,7 @@ Senders that do not provide `as_awaitable` — which includes most senders produ ### Compile-time safety -The IoAwaitable protocol's 2-argument `await_suspend(coroutine_handle<>, io_env const*)` structurally enforces that the execution environment is provided at suspension time. The dependency is in the function signature — the compiler rejects any call site that does not provide it. +The ContextAwaitable protocol's 2-argument `await_suspend(coroutine_handle<>, await_context const*)` structurally enforces that the execution environment is provided at suspension time. The dependency is in the function signature — the compiler rejects any call site that does not provide it. In the sender/receiver model, environment availability is checked when a sender queries the receiver's environment inside `start()`. This check IS compile-time (it fails template instantiation if the query is unsupported), but it is opt-in: each sender must explicitly constrain its `connect` method. If the sender author forgets the constraint, the error appears as a deep template instantiation failure rather than a clear signature mismatch. P3164R4 [11] and P3557R2 [12] are addressing diagnostic quality for these errors but are not yet part of the C++26 standard. @@ -181,7 +181,7 @@ The sender model would need an equivalent mechanism (a way for `start()` to indi ### What the bridge columns demonstrate -The bridged columns represent the real cost that arises when a library returns one I/O type but the application uses the other execution model. A networking library built on IoAwaitables will pay the `as_sender` tax when consumed from a sender pipeline. Conversely, a sender-based I/O library will pay the `await_sender` tax when consumed from `capy::task`. +The bridged columns represent the real cost that arises when a library returns one I/O type but the application uses the other execution model. A networking library built on ContextAwaitables will pay the `as_sender` tax when consumed from a sender pipeline. Conversely, a sender-based I/O library will pay the `await_sender` tax when consumed from `capy::task`. Both bridges are designed for universal correctness: @@ -206,7 +206,7 @@ This benchmark measures per-operation overhead for sequential I/O in a tight loo **Execution models** (one per table): - **sender/receiver pipeline** — Pure sender pipeline using stdexec's `repeat_until` [19] + `let_value`. No coroutines. Driven by `sender_thread_pool` via `sync_wait`. The `repeat_until` algorithm wraps each iteration with a `trampoline_scheduler` [19] that bounds recursion depth and stack consumption, preventing stack overflow from repeated inline completions. -- **capy::task** — Capy's coroutine task, driven by `capy::thread_pool`. Natively consumes IoAwaitables. +- **capy::task** — Capy's coroutine task, driven by `capy::thread_pool`. Natively consumes ContextAwaitables. - **beman::execution::task** — Beman's P2300 coroutine task [1], driven by `sender_thread_pool`. Natively consumes senders. **Note:** `bex::task`'s `await_transform` checks `as_awaitable` on the sender (first-priority dispatch per [exec.as.awaitable]). When the sender provides an `as_awaitable` member — as the benchmark's `sndr_read_stream` does — the task calls it directly, bypassing `connect`/`start` entirely. Table 3's native sender column (Col A) therefore measures the `as_awaitable` path, not the full sender protocol. This is the best-case scenario for senders in coroutines. See *Table 3 and `as_awaitable`* in the Analysis section for implications. **Stream abstraction levels** (one per row): @@ -229,7 +229,7 @@ Both thread pools inherit from `boost::capy::execution_context`, providing the s - **capy::thread_pool** — Used in Table 2 Col A. Posts `continuation&` objects via intrusive linked list (zero allocation per post). - **sender_thread_pool** — Used in all other cells. Posts `work_item*` intrusively when the sender's operation state inherits `work_item` (zero allocation). Has no `post(coroutine_handle<>)` — P2300 execution contexts only expose `schedule()` [20], which returns a sender. To resume a coroutine on the scheduler, the caller must go through `schedule()` → `connect()` → `start()`, heap-allocating the operation state (one allocation per post). -The `schedule`/`connect`/`start` allocation path is used when IoAwaitables post through the executor adapter (Tables 1 and 3 Col B). This is a cross-protocol adaptation cost: the IoAwaitable produces a `coroutine_handle<>`, but P2300 has no way to accept a bare handle. The adapter must create a `scheduled_resume` operation state — `connect(schedule(sched), resume_receiver)` — and heap-allocate it because the coroutine is suspended and cannot host it. The operation state IS the queue node (inherits `work_item`), so no additional wrapping is needed, but the allocation is unavoidable. Real P2300 execution contexts (stdexec's `run_loop`, `static_thread_pool`) use the same intrusive queue pattern [6]. +The `schedule`/`connect`/`start` allocation path is used when ContextAwaitables post through the executor adapter (Tables 1 and 3 Col B). This is a cross-protocol adaptation cost: the ContextAwaitable produces a `coroutine_handle<>`, but P2300 has no way to accept a bare handle. The adapter must create a `scheduled_resume` operation state — `connect(schedule(sched), resume_receiver)` — and heap-allocate it because the coroutine is suspended and cannot host it. The operation state IS the queue node (inherits `work_item`), so no additional wrapping is needed, but the allocation is unavoidable. Real P2300 execution contexts (stdexec's `run_loop`, `static_thread_pool`) use the same intrusive queue pattern [6]. **Operation state recycling:** @@ -251,7 +251,7 @@ Each `co_await` suspends the coroutine and posts to the thread pool's work queue ## Bridge Implementations -### await_sender (sender → IoAwaitable) +### await_sender (sender → ContextAwaitable) Used in Table 2 Column B. Wraps a P2300 sender so it can be `co_await`'d inside a `capy::task`. @@ -259,21 +259,21 @@ Used in Table 2 Column B. Wraps a P2300 sender so it can be `co_await`'d inside **Synchronous completion safety:** The bridge uses an `std::atomic` exchange protocol. Both `await_suspend` (after calling `start()`) and the receiver's completion function call `done_.exchange(true, memory_order_acq_rel)`. Whichever side arrives second (sees `true` from the exchange) is responsible for resuming the coroutine. If the sender completes synchronously during `start()`, `await_suspend` detects this and returns the coroutine handle for symmetric transfer — the coroutine never actually suspends, avoiding stack corruption [5]. This is the same pattern used by stdexec's `__sender_awaitable` [15], which uses `std::atomic` with a compare-and-swap protocol and a `std::thread::id` check for the same purpose, and by beman::execution's `sender_awaitable` [1], which uses `atomic`. -**Result routing:** The bridge inspects the sender's error completion signatures at compile time. If the sender can complete with `set_error(std::error_code)`, `await_resume` returns `io_result` so the error code is a value, not an exception. Otherwise, `await_resume` returns the value directly and rethrows exceptions. +**Result routing:** The bridge inspects the sender's error completion signatures at compile time. If the sender can complete with `set_error(std::error_code)`, `await_resume` returns `results` so the error code is a value, not an exception. Otherwise, `await_resume` returns the value directly and rethrows exceptions. **Zero bridge allocations:** The operation state lives on the coroutine frame (via placement new into a sized buffer). The receiver resumes the coroutine directly — no posting through the executor. The 0 al/op for native senders confirms this. -### as_sender (IoAwaitable → sender) +### as_sender (ContextAwaitable → sender) -Used in Tables 1 and 3 Column B. Wraps an IoAwaitable so it can be consumed by the P2300 sender/receiver model. +Used in Tables 1 and 3 Column B. Wraps a ContextAwaitable so it can be consumed by the P2300 sender/receiver model. **Mechanism:** The bridge constructs a synthetic coroutine frame (`frame_cb`) — a 24-byte struct whose first two members (resume/destroy function pointers) match the coroutine frame ABI layout used by MSVC, GCC, and Clang. `coroutine_handle<>::from_address(&cb_)` produces a valid handle whose `.resume()` calls the bridge's completion callback. This avoids allocating an actual coroutine frame, unlike P2300's `connect-awaitable` which creates a bridge coroutine with a heap-allocated frame that is "not generally eligible for the heap-allocation elision optimization (HALO)" [3]. stdexec mitigates this in its `__connect_awaitable` implementation by pre-allocating the coroutine frame inline in the operation state's storage buffer (64 bytes on 64-bit systems), providing a manual HALO that avoids heap allocation when the frame fits [17]. P4126R0 [10] proposes standardizing the synthetic frame technique as a "universal continuation model." **Executor query:** The bridge obtains a Capy-compatible executor from the P2300 environment using the standard query forwarding mechanism [2, §33.9.4]. It defines a `get_io_executor` query CPO marked as a forwarding query (`forwarding_query(get_io_executor_t{})` returns `true`), ensuring it propagates through sender adapter chains via `FWD-ENV` [2, §33.9.3.5]. Since `starts_on` injects `sched_env` (which only answers `get_scheduler` and `get_domain`), the bridge queries `get_scheduler(env)` — which IS forwarded — then queries the scheduler itself: `scheduler.query(get_io_executor_t{})`. The scheduler returns a Capy executor by value, which the bridge stores in the operation state. No benchmark-specific types appear in the bridge code. -**`as_awaitable` customization:** The `awaitable_sender` provides an `as_awaitable(Promise&)` member, which is the first-priority dispatch in `[exec.as.awaitable]` [2, §33.9.11.8]. stdexec's implementation confirms this priority: the `as_awaitable` CPO dispatches via `__first_callable` with the member function check (`__with_member`) as the highest priority, followed by transformed sender member, simple awaitable, and finally generic sender wrapping [15]. When `co_await`'d inside a `bex::task`, beman's `await_transform` calls this member instead of wrapping the sender in `sender_awaitable`. The member creates a standard awaitable that calls the IoAwaitable's 2-argument `await_suspend(handle, io_env const*)` directly, adapting it to the standard 1-argument protocol. This eliminates a double bridge (IoAwaitable → sender → `sender_awaitable` → awaitable) that would otherwise add connect/start/variant/atomic overhead. +**`as_awaitable` customization:** The `awaitable_sender` provides an `as_awaitable(Promise&)` member, which is the first-priority dispatch in `[exec.as.awaitable]` [2, §33.9.11.8]. stdexec's implementation confirms this priority: the `as_awaitable` CPO dispatches via `__first_callable` with the member function check (`__with_member`) as the highest priority, followed by transformed sender member, simple awaitable, and finally generic sender wrapping [15]. When `co_await`'d inside a `bex::task`, beman's `await_transform` calls this member instead of wrapping the sender in `sender_awaitable`. The member creates a standard awaitable that calls the ContextAwaitable's 2-argument `await_suspend(handle, await_context const*)` directly, adapting it to the standard 1-argument protocol. This eliminates a double bridge (ContextAwaitable → sender → `sender_awaitable` → awaitable) that would otherwise add connect/start/variant/atomic overhead. -**Completion routing:** The `frame_cb` callback calls `await_resume()` on the IoAwaitable and routes the result through P2300 completion channels based on the return type: `void` → `set_value()`, `error_code` → `set_value()`/`set_error(ec)`, other types → `set_value(T)`. +**Completion routing:** The `frame_cb` callback calls `await_resume()` on the ContextAwaitable and routes the result through P2300 completion channels based on the return type: `void` → `set_value()`, `error_code` → `set_value()`/`set_error(ec)`, other types → `set_value(T)`. ## References diff --git a/doc/lint/README.md b/doc/lint/README.md index 1c022a726..37431f293 100644 --- a/doc/lint/README.md +++ b/doc/lint/README.md @@ -63,7 +63,7 @@ name would make that gate match nothing while still exiting 0. The gate prints one entry per finding — location, rule, message, and a quote: ``` -ERROR include/boost/capy/concept/io_awaitable.hpp:68 +ERROR include/boost/capy/concept/context_awaitable.hpp:68 [sentence_length C2] sentence over 25 words (30) "_Effects_: If this operations instructs a coroutine to be resumed, i ... anner specific to `A`." ``` @@ -75,7 +75,7 @@ Three things make that possible, and each has a constraint worth knowing: `{file, line, excerpt}`. The comparator always passes `--details`; the reseed never does, so a committed `baseline.json` still contains no line numbers and cannot churn. - **Docstring findings are reported against the header, not the generated corpus.** A finding - in `lint/.docstrings/concept/io_awaitable.hpp.adoc` is unactionable — nobody edits that file. + in `lint/.docstrings/concept/context_awaitable.hpp.adoc` is unactionable — nobody edits that file. `extract-docstrings.mjs` writes a `.adoc.lines.json` sidecar mapping output-line ranges back to the line in the `.hpp` where each doc comment starts, and the reporter refines within the block by locating the excerpt in the header text. The sidecar is written *beside* the diff --git a/doc/lint/RESEARCH-docstring-examples.md b/doc/lint/RESEARCH-docstring-examples.md index 2c84a0748..21969cc8e 100644 --- a/doc/lint/RESEARCH-docstring-examples.md +++ b/doc/lint/RESEARCH-docstring-examples.md @@ -34,7 +34,7 @@ Representative blocks (chosen to show the range, not cherry-picked for compilabi @code task example() { - std::vector> reads; + std::vector>> reads; for (auto& buf : buffers) reads.push_back(stream.read_some(buf)); auto result = co_await when_any(std::move(reads)); @@ -259,7 +259,7 @@ not a hypothetical risk. Two of the twelve were not drift but active misinformat in one TU (`async_mutex`), a comment-only `if` body followed by `else` (`cond`, plain syntax error), `.data()` called on a value that has no such member for the sequence concepts in play (`test::buffer_to_string`), symbols (`route_params`, `route::next`) that exist in none of - capy, corosio, or burl (`io_task`), a mock object constructed outside the `fuse::armed` retry + capy, corosio, or burl (`task>`), a mock object constructed outside the `fuse::armed` retry loop so state leaked across rounds and the trailing comment went stale (`test::stream`, `test::write_stream`), a discarded partial-write result under a concept that documents partial writes as normal (`Stream`), and a redeclared local plus undeclared placeholder names diff --git a/doc/lint/baseline.json b/doc/lint/baseline.json index 533f35527..3a6b2f679 100644 --- a/doc/lint/baseline.json +++ b/doc/lint/baseline.json @@ -16,12 +16,12 @@ "modules/ROOT/pages/4.coroutines/4d.executors.adoc:#1:Vale.Spelling", "modules/ROOT/pages/4.coroutines/4d.executors.adoc:#2:Vale.Spelling", "modules/ROOT/pages/4.coroutines/4d.executors.adoc:#3:Vale.Spelling", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#1:Capy.NoFluff", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#1:Vale.Spelling", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#2:Capy.NoFluff", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#2:Vale.Spelling", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#3:Vale.Spelling", - "modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#4:Vale.Spelling", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#1:Capy.NoFluff", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#1:Vale.Spelling", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#2:Capy.NoFluff", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#2:Vale.Spelling", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#3:Vale.Spelling", + "modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#4:Vale.Spelling", "modules/ROOT/pages/4.coroutines/4f.cancellation.adoc:#1:Vale.Spelling", "modules/ROOT/pages/4.coroutines/4f.cancellation.adoc:#2:Vale.Spelling", "modules/ROOT/pages/4.coroutines/4g.composition.adoc:#1:Vale.Spelling", @@ -108,21 +108,21 @@ "lint/.docstrings/concept/buffer_archetype.hpp.adoc:#3:Vale.Spelling", "lint/.docstrings/concept/execution_context.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/concept/executor.hpp.adoc:#1:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#1:Capy.Terminology", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#1:Google.Colons", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#1:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#2:Google.Colons", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#2:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#3:Google.Colons", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#3:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#4:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#5:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#6:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#7:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#8:Vale.Spelling", - "lint/.docstrings/concept/io_awaitable.hpp.adoc:#9:Vale.Spelling", - "lint/.docstrings/concept/io_runnable.hpp.adoc:#1:Vale.Spelling", - "lint/.docstrings/concept/io_runnable.hpp.adoc:#2:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#1:Capy.Terminology", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#1:Google.Colons", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#1:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#2:Google.Colons", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#2:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#3:Google.Colons", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#3:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#4:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#5:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#6:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#7:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#8:Vale.Spelling", + "lint/.docstrings/concept/context_awaitable.hpp.adoc:#9:Vale.Spelling", + "lint/.docstrings/concept/context_runnable.hpp.adoc:#1:Vale.Spelling", + "lint/.docstrings/concept/context_runnable.hpp.adoc:#2:Vale.Spelling", "lint/.docstrings/concept/read_stream.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/concept/write_stream.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/cond.hpp.adoc:#1:Vale.Spelling", @@ -174,9 +174,9 @@ "lint/.docstrings/ex/immediate.hpp.adoc:#7:Vale.Spelling", "lint/.docstrings/ex/immediate.hpp.adoc:#8:Vale.Spelling", "lint/.docstrings/ex/immediate.hpp.adoc:#9:Vale.Spelling", - "lint/.docstrings/ex/io_awaitable_promise_base.hpp.adoc:#1:Vale.Spelling", - "lint/.docstrings/ex/io_awaitable_promise_base.hpp.adoc:#2:Vale.Spelling", - "lint/.docstrings/ex/io_env.hpp.adoc:#1:Vale.Spelling", + "lint/.docstrings/ex/context_awaitable_promise_base.hpp.adoc:#1:Vale.Spelling", + "lint/.docstrings/ex/context_awaitable_promise_base.hpp.adoc:#2:Vale.Spelling", + "lint/.docstrings/ex/await_context.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/ex/recycling_memory_resource.hpp.adoc:#10:Vale.Spelling", "lint/.docstrings/ex/recycling_memory_resource.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/ex/recycling_memory_resource.hpp.adoc:#2:Vale.Spelling", @@ -268,7 +268,6 @@ "lint/.docstrings/io/any_write_stream.hpp.adoc:#4:Vale.Spelling", "lint/.docstrings/io/write_now.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/io/write_now.hpp.adoc:#2:Vale.Spelling", - "lint/.docstrings/io_task.hpp.adoc:#1:Vale.Spelling", "lint/.docstrings/quitter.hpp.adoc:#10:Vale.Spelling", "lint/.docstrings/quitter.hpp.adoc:#1:Google.OxfordComma", "lint/.docstrings/quitter.hpp.adoc:#1:Vale.Spelling", @@ -384,13 +383,13 @@ ] }, "fingerprints": [ - "C2:lint/.docstrings/concept/io_awaitable.hpp.adoc:#1:sentence over 25 words", - "C2:lint/.docstrings/concept/io_awaitable.hpp.adoc:#2:sentence over 25 words", - "C2:lint/.docstrings/concept/io_awaitable.hpp.adoc:#3:sentence over 25 words", - "C2:lint/.docstrings/concept/io_awaitable.hpp.adoc:#4:sentence over 25 words", + "C2:lint/.docstrings/concept/context_awaitable.hpp.adoc:#1:sentence over 25 words", + "C2:lint/.docstrings/concept/context_awaitable.hpp.adoc:#2:sentence over 25 words", + "C2:lint/.docstrings/concept/context_awaitable.hpp.adoc:#3:sentence over 25 words", + "C2:lint/.docstrings/concept/context_awaitable.hpp.adoc:#4:sentence over 25 words", "C2:modules/ROOT/pages/2.cpp20-coroutines/2b.syntax.adoc:#1:sentence over 25 words", "C2:modules/ROOT/pages/2.cpp20-coroutines/2b.syntax.adoc:#2:sentence over 25 words", - "C2:modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc:#1:sentence over 25 words", + "C2:modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc:#1:sentence over 25 words", "advisory-C2:modules/ROOT/pages/9.design/9a.CapyLayering.adoc:#1:sentence over 25 words", "advisory-C2:modules/ROOT/pages/9.design/9a.CapyLayering.adoc:#2:sentence over 25 words", "advisory-C2:modules/ROOT/pages/9.design/9a.CapyLayering.adoc:#3:sentence over 25 words", @@ -513,7 +512,7 @@ "/capy/4.coroutines/4b.tasks.html:link-name:#_returning_values_with_co_return > a", "/capy/4.coroutines/4b.tasks.html:link-name:#_running_a_task > a", "/capy/4.coroutines/4b.tasks.html:link-name:#_symmetric_transfer > a", - "/capy/4.coroutines/4b.tasks.html:link-name:#io-result-and-io-task > a", + "/capy/4.coroutines/4b.tasks.html:link-name:#reporting-errors > a", "/capy/4.coroutines/4b.tasks.html:list:#toc > aside > div > div > nav > ul", "/capy/4.coroutines/4b.tasks.html:list:#toc > aside > div > div > nav > ul > ul", "/capy/4.coroutines/4b.tasks.html:scrollable-region-focusable:#content > article > div:nth-child(10) > div > div:nth-child(2) > div > pre", diff --git a/doc/lint/extract-docstrings.mjs b/doc/lint/extract-docstrings.mjs index 538dde6db..f443ee75c 100644 --- a/doc/lint/extract-docstrings.mjs +++ b/doc/lint/extract-docstrings.mjs @@ -26,9 +26,9 @@ // BOTH Doxygen doc-comment forms are extracted: `/** ... */` blocks and runs of // consecutive `///` lines. A run of `///` lines is ONE doc comment attached to // the declaration that follows it, which is how Doxygen and MrDocs read it, and -// MrDocs really does publish that prose — `io_result.hpp:49`'s +// MrDocs really does publish that prose — `results.hpp:49`'s // `/// The error code from the operation.` renders on both -// `reference/boost/capy/io_result.html` and `reference/boost/capy/io_result/ec.html`. +// `reference/boost/capy/results.html` and `reference/boost/capy/results/ec.html`. // Until 2026-08 only the `/** */` form was extracted, so 86 published doc lines // across 25 headers were invisible to every Vale rule and to the C2 checker, // including the four the Phase-4 exit promoted to merge-blocking gates. It was diff --git a/doc/modules/ROOT/nav.adoc b/doc/modules/ROOT/nav.adoc index 36ddca04f..cfde9a299 100644 --- a/doc/modules/ROOT/nav.adoc +++ b/doc/modules/ROOT/nav.adoc @@ -6,7 +6,7 @@ ** xref:4.coroutines/4b.tasks.adoc[The task Type] ** xref:4.coroutines/4c.launching.adoc[Starting Coroutines] ** xref:4.coroutines/4d.executors.adoc[Executors and Execution Contexts] -** xref:4.coroutines/4e.io-awaitable.adoc[The IoAwaitable Protocol] +** xref:4.coroutines/4e.context-awaitable.adoc[The ContextAwaitable Protocol] ** xref:4.coroutines/4f.cancellation.adoc[Stop Tokens and Cancellation] ** xref:4.coroutines/4g.composition.adoc[Concurrent Composition] ** xref:4.coroutines/4h.allocators.adoc[Frame Allocators] diff --git a/doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc b/doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc index c0efa88e2..853fa86fe 100644 --- a/doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc +++ b/doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc @@ -197,4 +197,4 @@ This generator: * Manages coroutine lifetime with RAII * Supports move semantics -These fundamentals prepare you for understanding Capy's cpp:task[task] type and the IoAwaitable protocol. Both build on standard coroutine machinery with executor affinity and stop token propagation. +These fundamentals prepare you for understanding Capy's cpp:task[task] type and the ContextAwaitable protocol. Both build on standard coroutine machinery with executor affinity and stop token propagation. diff --git a/doc/modules/ROOT/pages/4.coroutines/4.intro.adoc b/doc/modules/ROOT/pages/4.coroutines/4.intro.adoc index af9403e67..acf5f7973 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4.intro.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4.intro.adoc @@ -22,7 +22,7 @@ Capy's coroutine model is built around a single principle: asynchronous code sho cpp:run_async[], and binding child tasks with cpp:run[]. * xref:4.coroutines/4d.executors.adoc[Executors and Execution Contexts] -- Executors, execution contexts, thread pools, and strands. -* xref:4.coroutines/4e.io-awaitable.adoc[The IoAwaitable Protocol] -- How the executor and +* xref:4.coroutines/4e.context-awaitable.adoc[The ContextAwaitable Protocol] -- How the executor and stop token propagate through a chain of awaited coroutines. * xref:4.coroutines/4f.cancellation.adoc[Stop Tokens and Cancellation] -- Cooperative cancellation with `std::stop_token`, and how Capy tasks observe it. diff --git a/doc/modules/ROOT/pages/4.coroutines/4b.tasks.adoc b/doc/modules/ROOT/pages/4.coroutines/4b.tasks.adoc index 1e3a5e31d..edc8da5c0 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4b.tasks.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4b.tasks.adoc @@ -43,23 +43,23 @@ include::example$snippets/4b_tasks.cpp[tag=returning] For cpp:task[task], you can either use `co_return;` explicitly or let execution fall off the end of the function body. -[#io-result-and-io-task] -== Reporting Errors: io_result and io_task +[#reporting-errors] +== Reporting Errors: results -I/O operations report an expected failure as a value rather than an exception. They return the error alongside the result, in a cpp:io_result[io_result]: a `std::tuple` holding a `std::error_code` followed by zero or more payload values. Exceptions remain for genuine errors, such as a failed frame allocation. +I/O operations report an expected failure as a value rather than an exception. They return the error alongside the result, in a cpp:results[results]: a `std::tuple` holding a `std::error_code` followed by zero or more payload values. Exceptions remain for genuine errors, such as a failed frame allocation. -cpp:io_task[io_task] names a task returning one of those results: +A coroutine that reports errors this way returns cpp:task[task]`>`: [source,cpp] ---- -include::example$snippets/4b_tasks.cpp[tag=io_task] +include::example$snippets/4b_tasks.cpp[tag=results] ---- -Because cpp:io_result[] is a `std::tuple`, the whole standard tuple API applies: structured bindings, `std::tie`, `std::apply`, `std::get`, `std::tuple_cat`, comparisons, and tuple assignment. The error code is the first element, so `auto [ec, n] = co_await s.read_some(buf);` destructures it directly, and `std::tie` rebinds into existing variables without introducing new ones. Always test the error code before reading a payload; a payload's meaning when it is set is defined by the operation that produced it. +Because cpp:results[] is a `std::tuple`, the whole standard tuple API applies: structured bindings, `std::tie`, `std::apply`, `std::get`, `std::tuple_cat`, comparisons, and tuple assignment. The error code is the first element, so `auto [ec, n] = co_await s.read_some(buf);` destructures it directly, and `std::tie` rebinds into existing variables without introducing new ones. Always test the error code before reading a payload; a payload's meaning when it is set is defined by the operation that produced it. -For cpp:io_result[io_result<>] -- no payload -- a `std::error_code` converts implicitly, so `co_return some_ec;` compiles. With payloads present you must supply the whole result, as `count_ready` shows. +For cpp:results[results<>] -- no payload -- a `std::error_code` converts implicitly, so `co_return some_ec;` compiles. With payloads present you must supply the whole result, as `count_ready` shows. -These two names are the vocabulary the stream concepts and the concurrent combinators are written in. xref:4.coroutines/4g.composition.adoc[Concurrent Composition] and xref:6.streams/6.intro.adoc[Streams] both assume them. +This is the vocabulary the stream concepts and the concurrent combinators are written in. xref:4.coroutines/4g.composition.adoc[Concurrent Composition] and xref:6.streams/6.intro.adoc[Streams] both assume them. == Running a Task diff --git a/doc/modules/ROOT/pages/4.coroutines/4d.executors.adoc b/doc/modules/ROOT/pages/4.coroutines/4d.executors.adoc index 98eda5afc..7b84ddda0 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4d.executors.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4d.executors.adoc @@ -19,7 +19,7 @@ Start this on a strand, and every resumption—after `conn.read()` and after `co Without the invariant, the coroutine could resume after `co_await conn.read()` on an `io_uring` completion thread, a pool thread, or wherever the I/O subsystem completed the operation. Correct code would then need either a mutex around every access to shared state or an explicit _resume-on-this-strand_ step after every `co_await`. A mutex defeats the purpose of the strand, and a single forgotten resume step reintroduces the data race. -Because the invariant holds, the safe behavior is automatic and the unsafe behavior does not compile. Awaiting a _plain_ awaitable—one that could resume the coroutine on any thread—is rejected. See xref:4.coroutines/4e.io-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] for why, and for the explicit escape hatch. +Because the invariant holds, the safe behavior is automatic and the unsafe behavior does not compile. Awaiting a _plain_ awaitable—one that could resume the coroutine on any thread—is rejected. See xref:4.coroutines/4e.context-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] for why, and for the explicit escape hatch. === How the Invariant Is Maintained diff --git a/doc/modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc b/doc/modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc similarity index 74% rename from doc/modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc rename to doc/modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc index 365da976c..045105bf5 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4e.io-awaitable.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4e.context-awaitable.adoc @@ -1,4 +1,4 @@ -= The IoAwaitable Protocol += The ContextAwaitable Protocol :page-mode: explanation == The Problem: Context Propagation @@ -7,7 +7,7 @@ Standard {cpp}20 coroutines define awaiters with this `await_suspend` signature: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=std_await_suspend,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=std_await_suspend,indent=0] ---- The awaiter receives only a handle to the suspended coroutine. But real applications need more: @@ -20,7 +20,7 @@ The awaiter receives only a handle to the suspended coroutine. But real applicat ==== *Nothing on this page is specific to I/O.* An executor, a stop token, and an allocator are what any asynchronous operation needs. It makes no difference whether the operation waits on a socket, a GPU queue, a timer, or another thread. -The `Io` in cpp:IoAwaitable[] reflects where the protocol was first used, Corosio's I/O types, rather than a restriction on where it applies. Read the name as "an awaitable that carries its execution environment." +The `Io` in cpp:ContextAwaitable[] reflects where the protocol was first used, Corosio's I/O types, rather than a restriction on where it applies. Read the name as "an awaitable that carries its execution environment." ==== === Backward Query Approach @@ -42,16 +42,16 @@ the execution environment: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=two_arg_await_suspend,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=two_arg_await_suspend,indent=0] ---- This signature receives: * `h` — The coroutine handle (as in standard awaiters) * `env` — The execution environment containing: -** cpp:io_env::executor[env->executor] — The caller's executor for dispatching completions -** cpp:io_env::stop_token[env->stop_token] — A stop token for cooperative cancellation -** cpp:io_env::frame_allocator[env->frame_allocator] — An optional frame allocator +** cpp:await_context::executor[env->executor] — The caller's executor for dispatching completions +** cpp:await_context::stop_token[env->stop_token] — A stop token for cooperative cancellation +** cpp:await_context::frame_allocator[env->frame_allocator] — An optional frame allocator Following the convention of awaiters in C++, the return type of the function has to be one of: @@ -59,28 +59,28 @@ Following the convention of awaiters in C++, the return type of the function has * `bool`, * `std::coroutine_handle

` for any type `P`. -Many types modelling cpp:IoAwaitable[] return `std::coroutine_handle<>` to enable +Many types modelling cpp:ContextAwaitable[] return `std::coroutine_handle<>` to enable xref:2.cpp20-coroutines/2d.advanced.adoc#_symmetric_transfer[symmetric transfer]. -== IoAwaitable Concept +== ContextAwaitable Concept -Concept cpp:IoAwaitable[] specifies all requirements on a type that can be ``co_await``ed +Concept cpp:ContextAwaitable[] specifies all requirements on a type that can be ``co_await``ed in Capy-coroutines. Apart from the two-argument `await_suspend`, the type also needs to provide the two member functions specific to awaiters: `await_ready` and `await_resume`. -In order to model cpp:IoAwaitable[] a type need not be associated with any coroutine. In fact, -this is the case for a number of cpp:IoAwaitable[]s in Corosio. +In order to model cpp:ContextAwaitable[] a type need not be associated with any coroutine. In fact, +this is the case for a number of cpp:ContextAwaitable[]s in Corosio. -== IoRunnable Concept +== ContextRunnable Concept -For tasks that can be started from non-coroutine contexts, the cpp:IoRunnable[] concept refines cpp:IoAwaitable[] and requires a `promise_type` plus the following: +For tasks that can be started from non-coroutine contexts, the cpp:ContextRunnable[] concept refines cpp:ContextAwaitable[] and requires a `promise_type` plus the following: * `handle()`: Access the typed coroutine handle * `release()`: Transfer ownership of the frame * `exception()`: Check for captured exceptions (on the promise) * `result()`: Access the result value (on the promise, for non-void tasks) * `set_continuation()`: Set the continuation handle (on the promise) -* `set_environment()`: Inject the `io_env` (on the promise) +* `set_environment()`: Inject the `await_context` (on the promise) These methods exist because launcher functions like cpp:run_async[] cannot `co_await` the task directly. The trampoline must be allocated before the task type is known, so it type-erases the task through function pointers. The trampoline therefore needs a common API to manage lifetime and extract results. @@ -97,7 +97,7 @@ When you write `co_await child_task()` inside a cpp:task[task]: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=context_flow,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=context_flow,indent=0] ---- The child receives the parent's executor and stop token automatically. @@ -107,29 +107,29 @@ The child receives the parent's executor and stop token automatically. Forward propagation offers several advantages: * *Decoupling* — Awaitables don't need to know caller's promise type -* *Composability* — Any IoAwaitable works with any IoRunnable task +* *Composability* — Any ContextAwaitable works with any ContextRunnable task * *Explicit flow* — Context flows downward through the call chain, not queried upward This design enables Capy's type-erased wrappers (cpp:any_stream[], etc.) to work without knowing the concrete executor type. == A Vocabulary for Coroutine Interop -Because the protocol is just a two-argument `await_suspend`, cpp:IoAwaitable[] is more than an internal mechanism—it is a _vocabulary type_ for interoperation. Any coroutine library that speaks the protocol can propagate execution environment across a `co_await` boundary without knowing the other side's concrete task type. +Because the protocol is just a two-argument `await_suspend`, cpp:ContextAwaitable[] is more than an internal mechanism—it is a _vocabulary type_ for interoperation. Any coroutine library that speaks the protocol can propagate execution environment across a `co_await` boundary without knowing the other side's concrete task type. This is the interop problem framed on the xref:index.adoc[home page]. A shared protocol replaces the set of pairwise adapters that separate coroutine libraries would otherwise need to talk to one another. Capy is the reference implementation of that protocol. -== Implementing Custom IoAwaitables +== Implementing Custom ContextAwaitables -To create a custom IoAwaitable: +To create a custom ContextAwaitable: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=my_awaitable] +include::example$snippets/4e_context_awaitable.cpp[tag=my_awaitable] ---- The key points: -1. Store the `io_env` as a pointer (`io_env const*`), never a copy. Launcher functions guarantee the `io_env` outlives the awaitable's operation. +1. Store the `await_context` as a pointer (`await_context const*`), never a copy. Launcher functions guarantee the `await_context` outlives the awaitable's operation. 2. To resume the caller, wrap its handle in a cpp:continuation[] and pass that to the executor's `post` (or `dispatch`). These take a `continuation&`, not a raw `coroutine_handle`. Store the `continuation` in the awaitable so it keeps a stable address until the executor dequeues and resumes it. The executor links continuations intrusively, so a temporary would dangle. 3. Respect the stop token for cancellation @@ -146,34 +146,34 @@ Post the resume through the executor instead of resuming inline: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=stoppable_awaitable] +include::example$snippets/4e_context_awaitable.cpp[tag=stoppable_awaitable] ---- The incorrect pattern—which compiles and appears to work but causes memory corruption—looks like this: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=wrong_stop_callback,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=wrong_stop_callback,indent=0] ---- See xref:4.coroutines/4f.cancellation.adoc#stoppable-awaitables[Implementing Stoppable Awaitables] for a complete example. For a production implementation of this exact pattern, read the source of cpp:async_waker::wait_awaiter[]. It registers a stop callback that posts the resume through the executor, and arbitrates between wakeup and cancellation with a single atomic claim. -== Running a Custom IoAwaitable +== Running a Custom ContextAwaitable This awaitable produces a value and resumes the caller by posting its cpp:continuation[] through the caller's executor: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=runnable_awaitable] +include::example$snippets/4e_context_awaitable.cpp[tag=runnable_awaitable] ---- To run that task from ordinary, non-coroutine code, hand it to cpp:run_async[] with an executor and a completion handler: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=run_awaitable,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=run_awaitable,indent=0] ---- The task runs on the cpp:thread_pool[] executor. When the awaitable posts its continuation, the pool resumes the task, `await_resume` returns the value, and it arrives at the completion handler—here, `5`. @@ -187,16 +187,16 @@ Capy rejects it at compile time: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=reject_plain,indent=0] +include::example$snippets/4e_context_awaitable.cpp[tag=reject_plain,indent=0] ---- This is intentional. A plain awaitable receives only the coroutine handle; it can resume the coroutine on any thread by calling `handle.resume()` directly. That silently breaks the xref:4.coroutines/4d.executors.adoc#the-same-executor-invariant[same-executor invariant]—the coroutine could wake on a foreign completion thread, leaving shared state you believed was strand-protected exposed to races. Rejecting such an awaitable at compile time prevents that. The constraint does not lock you in; it requires environment propagation to be explicit rather than silently dropped. -The escape hatch is to wrap the foreign awaitable (or callback, or future) in a small cpp:IoAwaitable[]. That awaitable captures the executor and re-posts the resumption through it: +The escape hatch is to wrap the foreign awaitable (or callback, or future) in a small cpp:ContextAwaitable[]. That awaitable captures the executor and re-posts the resumption through it: [source,cpp] ---- -include::example$snippets/4e_io_awaitable.cpp[tag=foreign_bridge] +include::example$snippets/4e_context_awaitable.cpp[tag=foreign_bridge] ---- The single rule that makes any bridge correct: *on completion, post through `env->executor` instead of resuming the handle inline.* @@ -209,6 +209,6 @@ Capy does not provide a generic `co_await foreign_awaitable(x)` that adapts _arb Two complete, buildable bridges live in the examples: -* xref:8.examples/8o.sender-bridge.adoc[Bridging a P2300 Sender] — `await_sender` adapts a `std::execution` sender, mapping its completion channels onto cpp:io_result[] and posting the resumption through the executor. -* xref:8.examples/8p.asio-use-capy.adoc[Calling Asio from a Capy Coroutine] — the `use_capy` completion token turns any Asio async operation into an cpp:IoAwaitable[]. +* xref:8.examples/8o.sender-bridge.adoc[Bridging a P2300 Sender] — `await_sender` adapts a `std::execution` sender, mapping its completion channels onto cpp:results[] and posting the resumption through the executor. +* xref:8.examples/8p.asio-use-capy.adoc[Calling Asio from a Capy Coroutine] — the `use_capy` completion token turns any Asio async operation into a cpp:ContextAwaitable[]. diff --git a/doc/modules/ROOT/pages/4.coroutines/4f.cancellation.adoc b/doc/modules/ROOT/pages/4.coroutines/4f.cancellation.adoc index 798800793..a3c708333 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4f.cancellation.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4f.cancellation.adoc @@ -137,7 +137,7 @@ Coroutines have a propagation problem: passing a stop token explicitly to every === Capy's Answer: Automatic Propagation -Capy propagates stop tokens downward through `co_await`. When you await a task, the IoAwaitable protocol passes the current stop token to the child: +Capy propagates stop tokens downward through `co_await`. When you await a task, the ContextAwaitable protocol passes the current stop token to the child: [source,cpp] ---- diff --git a/doc/modules/ROOT/pages/4.coroutines/4g.composition.adoc b/doc/modules/ROOT/pages/4.coroutines/4g.composition.adoc index 641a8bd30..189bc54ca 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4g.composition.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4g.composition.adoc @@ -3,7 +3,7 @@ Headers: `` and ``. cpp:async_waker[] on this page is ``. -Both combinators take cpp:io_task[] children and return their results as a single cpp:io_result[]. If those two names are new, read xref:4.coroutines/4b.tasks.adoc#io-result-and-io-task[Reporting Errors] first -- everything below is expressed in them. +Both combinators take cpp:task[task]`>` children and return their results as a single cpp:results[]. If that type is new, read xref:4.coroutines/4b.tasks.adoc#reporting-errors[Reporting Errors] first -- everything below is expressed in them. == Overview @@ -23,7 +23,7 @@ include::example$snippets/4g_composition.cpp[tag=concurrent,indent=0] == when_all: Wait for All Tasks -cpp:when_all[] starts multiple cpp:io_task[] children concurrently and waits for all of them to complete. It returns cpp:task[task]`>`, a single `ec` plus the flattened payloads: +cpp:when_all[] starts multiple cpp:task[task]`>` children concurrently and waits for all of them to complete. It returns cpp:task[task]`>`, a single `ec` plus the flattened payloads: [source,cpp] ---- @@ -32,18 +32,18 @@ include::example$snippets/4g_composition.cpp[tag=when_all_basic,indent=0] === Result Type -cpp:when_all[] returns cpp:io_result[io_result]`` where each `Ri` is the child's payload flattened: `io_result` contributes `T`, `io_result<>` contributes `tuple<>`. Check `ec` first; values are only meaningful when `!ec`. +cpp:when_all[] returns cpp:results[results]`` where each `Ri` is the child's payload flattened: `results` contributes `T`, `results<>` contributes `tuple<>`. Check `ec` first; values are only meaningful when `!ec`. -=== Void io_tasks +=== Payload-free children -cpp:io_task[io_task<>] children contribute `tuple<>` to the result: +cpp:task[task]`>` children contribute `tuple<>` to the result: [source,cpp] ---- include::example$snippets/4g_composition.cpp[tag=when_all_void_mix,indent=0] ---- -When all children are cpp:io_task[io_task<>], just check the error code: +When all children are cpp:task[task]`>`, just check the error code: [source,cpp] ---- @@ -52,11 +52,11 @@ include::example$snippets/4g_composition.cpp[tag=when_all_all_void,indent=0] === Error Handling -I/O errors are reported through the error code that leads the cpp:io_result[io_result]. When any child returns a non-zero error code: +I/O errors are reported through the error code that leads the cpp:results[results]. When any child returns a non-zero error code: 1. Stop is requested for sibling tasks 2. All tasks complete (or respond to stop) -3. The first `ec` (in completion order, not input order) is propagated in the outer cpp:io_result[io_result] +3. The first `ec` (in completion order, not input order) is propagated in the outer cpp:results[results] [source,cpp] ---- @@ -81,7 +81,7 @@ include::example$snippets/4g_composition.cpp[tag=stop_propagation,indent=0] == when_any: First-to-Succeed Wins -cpp:when_any[] starts multiple cpp:io_task[] children concurrently and returns when the first one *succeeds* (`!ec`): +cpp:when_any[] starts multiple cpp:task[task]`>` children concurrently and returns when the first one *succeeds* (`!ec`): [source,cpp] ---- diff --git a/doc/modules/ROOT/pages/4.coroutines/4h.allocators.adoc b/doc/modules/ROOT/pages/4.coroutines/4h.allocators.adoc index cc7b17257..59e70e772 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4h.allocators.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4h.allocators.adoc @@ -271,4 +271,4 @@ cpp:frame_alloc_mixin[] (in ``) supplies `o * Bypass virtual dispatch when that allocator is the default recycling memory resource. * Store the resolved allocator pointer at the tail of each frame, so deallocation uses the correct resource even if the thread-local allocator has since changed. -This is the same strategy used internally by cpp:io_awaitable_promise_base[]. Use the mixin directly when your promise type does not need the full environment and continuation support that `io_awaitable_promise_base` provides. The allocation fast path uses thread-local storage and needs no synchronization; the global pool fallback is mutex-protected. +This is the same strategy used internally by cpp:context_awaitable_promise_base[]. Use the mixin directly when your promise type does not need the full environment and continuation support that `context_awaitable_promise_base` provides. The allocation fast path uses thread-local storage and needs no synchronization; the global pool fallback is mutex-protected. diff --git a/doc/modules/ROOT/pages/4.coroutines/4j.lifetimes.adoc b/doc/modules/ROOT/pages/4.coroutines/4j.lifetimes.adoc index f2b5dcddd..9f12dae66 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4j.lifetimes.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4j.lifetimes.adoc @@ -121,14 +121,14 @@ they describe is not. == Inside an Awaitable -An awaitable is given an cpp:io_env[] pointer in `await_suspend`. The +An awaitable is given an cpp:await_context[] pointer in `await_suspend`. The environment belongs to the launcher that started the chain and lives until the awaitable's coroutine resumes or is unwound, so the awaitable can keep the pointer. The cpp:continuation[] it hands to an executor must keep a stable address until the executor runs it, so it belongs in the awaitable, not on the stack. -xref:4.coroutines/4e.io-awaitable.adoc#_implementing_custom_ioawaitables[Implementing Custom IoAwaitables] +xref:4.coroutines/4e.context-awaitable.adoc#_implementing_custom_contextawaitables[Implementing Custom ContextAwaitables] lists these rules, and the rules for stop callbacks. == Who Owns a Suspended Coroutine @@ -223,7 +223,7 @@ include::example$snippets/4j_lifetimes.cpp[tag=shutdown_order,indent=0] | The storage outlives the operation; await it, do not store it. | Awaitable -| Keep the `io_env` pointer; keep the `continuation` in the awaitable. +| Keep the `await_context` pointer; keep the `continuation` in the awaitable. | Suspended coroutine | Its holder resumes it or abandons it; never `destroy()` an awaited handle. diff --git a/doc/modules/ROOT/pages/7.testing/7a.drivers.adoc b/doc/modules/ROOT/pages/7.testing/7a.drivers.adoc index 3a88ac8e0..baf040c02 100644 --- a/doc/modules/ROOT/pages/7.testing/7a.drivers.adoc +++ b/doc/modules/ROOT/pages/7.testing/7a.drivers.adoc @@ -126,7 +126,7 @@ include::example$snippets/7a_drivers.cpp[tag=early_return,indent=0] === Coroutine Support -`armed()` detects when the test lambda returns an cpp:IoRunnable[] (such as +`armed()` detects when the test lambda returns a cpp:ContextRunnable[] (such as cpp:task[task]) and drives it to completion via `run_blocking` internally. You do not need to call `run_blocking` yourself: diff --git a/doc/modules/ROOT/pages/8.examples/8f.timeout-cancellation.adoc b/doc/modules/ROOT/pages/8.examples/8f.timeout-cancellation.adoc index fddf6a864..3c0b90596 100644 --- a/doc/modules/ROOT/pages/8.examples/8f.timeout-cancellation.adoc +++ b/doc/modules/ROOT/pages/8.examples/8f.timeout-cancellation.adoc @@ -52,7 +52,7 @@ you spun up yourself to play the role of a clock, wakes it with cpp:async_waker: include::example$timeout-cancellation/timeout_cancellation.cpp[tag=fetch_channel] ---- -cpp:async_waker::wait[wait]`()` returns an cpp:io_result[io_result<>] that is empty on wakeup and carries +cpp:async_waker::wait[wait]`()` returns a cpp:results[results<>] that is empty on wakeup and carries cpp:cond::canceled[cond::canceled] if the environment's stop token fires first. A wakeup with no waiter present is latched as one pending token, so the wake-before-wait race is benign. Extra wakes collapse into that single @@ -63,7 +63,7 @@ callback from another library. === Racing a Fetch Against a Deadline -`demo_timeout` builds two cpp:io_task[] children, each just a waker wait, and +`demo_timeout` builds two cpp:task[task]`>` children, each just a waker wait, and races them with cpp:when_any[]: [source,cpp] diff --git a/doc/modules/ROOT/pages/8.examples/8g.parallel-fetch.adoc b/doc/modules/ROOT/pages/8.examples/8g.parallel-fetch.adoc index 611267549..300553dac 100644 --- a/doc/modules/ROOT/pages/8.examples/8g.parallel-fetch.adoc +++ b/doc/modules/ROOT/pages/8.examples/8g.parallel-fetch.adoc @@ -33,16 +33,16 @@ target_link_libraries(parallel_fetch PRIVATE Boost::capy) include::example$parallel-fetch/parallel_fetch.cpp[tag=when_all_dashboard,indent=0] ---- -cpp:when_all[] requires children returning cpp:io_result[], so plain tasks are wrapped. The result is `io_result`, a single `ec` plus the flattened payloads in input order. +cpp:when_all[] requires children returning cpp:results[], so plain tasks are wrapped. The result is `results`, a single `ec` plus the flattened payloads in input order. -=== Void io_tasks +=== Payload-free children [source,cpp] ---- include::example$parallel-fetch/parallel_fetch.cpp[tag=when_all_void,indent=0] ---- -cpp:io_task[io_task<>] children return cpp:io_result[io_result<>] (an error code, no payload). Check `std::get<0>(r)` to detect failure. +cpp:task[task]`>` children return cpp:results[results<>] (an error code, no payload). Check `std::get<0>(r)` to detect failure. === Error Propagation @@ -51,7 +51,7 @@ cpp:io_task[io_task<>] children return cpp:io_result[io_result<>] (an error code include::example$parallel-fetch/parallel_fetch.cpp[tag=when_all_errors,indent=0] ---- -I/O errors are reported via `ec` in the cpp:io_result[io_result]. Thrown exceptions are captured separately. Upon error, cancellation is requested and the first exception is rethrown after all tasks complete. +I/O errors are reported via `ec` in the cpp:results[results]. Thrown exceptions are captured separately. Upon error, cancellation is requested and the first exception is rethrown after all tasks complete. == Output diff --git a/doc/modules/ROOT/pages/8.examples/8l.async-mutex.adoc b/doc/modules/ROOT/pages/8.examples/8l.async-mutex.adoc index 35809fb0d..ba722df8b 100644 --- a/doc/modules/ROOT/pages/8.examples/8l.async-mutex.adoc +++ b/doc/modules/ROOT/pages/8.examples/8l.async-mutex.adoc @@ -43,7 +43,7 @@ cpp:async_mutex[] is a coroutine-aware mutex. Unlike `std::mutex`, it suspends t include::example$async-mutex/async_mutex.cpp[tag=scoped_lock,indent=0] ---- -`scoped_lock()` returns an cpp:io_result[] with an error code and an RAII guard. The guard automatically releases the lock when it goes out of scope. If the operation is canceled (e.g., via a stop token), `ec` is set. +`scoped_lock()` returns a cpp:results[] with an error code and an RAII guard. The guard automatically releases the lock when it goes out of scope. If the operation is canceled (e.g., via a stop token), `ec` is set. === FIFO Fairness diff --git a/doc/modules/ROOT/pages/8.examples/8m.parallel-tasks.adoc b/doc/modules/ROOT/pages/8.examples/8m.parallel-tasks.adoc index 9345e4eb2..0486751c3 100644 --- a/doc/modules/ROOT/pages/8.examples/8m.parallel-tasks.adoc +++ b/doc/modules/ROOT/pages/8.examples/8m.parallel-tasks.adoc @@ -42,7 +42,7 @@ The range `[0, 10000)` is divided into 4 equal chunks, one per task. Each task c include::example$programs/8m_parallel_tasks_variadic.cpp[tag=when_all_variadic,indent=0] ---- -cpp:when_all[] starts all four tasks concurrently on the thread pool. The result is cpp:io_result[io_result]``, a single `ec` plus the four partial sums in input order. +cpp:when_all[] starts all four tasks concurrently on the thread pool. The result is cpp:results[results]``, a single `ec` plus the four partial sums in input order. === Observing Thread IDs diff --git a/doc/modules/ROOT/pages/8.examples/8o.sender-bridge.adoc b/doc/modules/ROOT/pages/8.examples/8o.sender-bridge.adoc index e8760c08d..38525cbd6 100644 --- a/doc/modules/ROOT/pages/8.examples/8o.sender-bridge.adoc +++ b/doc/modules/ROOT/pages/8.examples/8o.sender-bridge.adoc @@ -5,9 +5,9 @@ Awaiting a `std::execution` (P2300) sender from inside a Capy coroutine. == What This Example Shows -* How to `co_await` a foreign awaitable that does not implement the xref:4.coroutines/4e.io-awaitable.adoc[IoAwaitable protocol] +* How to `co_await` a foreign awaitable that does not implement the xref:4.coroutines/4e.context-awaitable.adoc[ContextAwaitable protocol] * How a bridge restores the xref:4.coroutines/4d.executors.adoc#the-same-executor-invariant[same-executor invariant] by posting the resumption through the caller's executor -* How a sender's value and error completion channels map onto cpp:io_result[] +* How a sender's value and error completion channels map onto cpp:results[] [NOTE] ==== @@ -16,14 +16,14 @@ This example is built only when `BOOST_CAPY_BUILD_P2300_EXAMPLES=ON` (it require == The Bridge -A P2300 sender is a foreign awaitable: it has no two-argument `await_suspend`, so a Capy cpp:task[] cannot `co_await` it directly. `await_sender` wraps it in an cpp:IoAwaitable[]. The key move is in the bridge's _receiver_: when the sender completes—on whatever thread its scheduler chose—it does not resume the coroutine inline. It stores the result and posts the continuation back through the caller's executor. From `example/sender-bridge/sender_awaitable.hpp`: +A P2300 sender is a foreign awaitable: it has no two-argument `await_suspend`, so a Capy cpp:task[] cannot `co_await` it directly. `await_sender` wraps it in a cpp:ContextAwaitable[]. The key move is in the bridge's _receiver_: when the sender completes—on whatever thread its scheduler chose—it does not resume the coroutine inline. It stores the result and posts the continuation back through the caller's executor. From `example/sender-bridge/sender_awaitable.hpp`: [source,cpp] ---- include::example$sender-bridge/sender_awaitable.hpp[tag=set_value,indent=0] ---- -`await_sender` inspects the sender's completion signatures. If the sender can complete with `set_error(std::error_code)`, the bridge yields cpp:io_result[io_result] so the error stays a value rather than an exception. Otherwise the bridge yields the value directly. +`await_sender` inspects the sender's completion signatures. If the sender can complete with `set_error(std::error_code)`, the bridge yields cpp:results[results] so the error stays a value rather than an exception. Otherwise the bridge yields the value directly. == Source Code @@ -46,5 +46,5 @@ result: 1764 == See Also -* xref:4.coroutines/4e.io-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] — the general pattern and why there is no universal bridge +* xref:4.coroutines/4e.context-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] — the general pattern and why there is no universal bridge * xref:8.examples/8p.asio-use-capy.adoc[Calling Asio from a Capy Coroutine] — the same technique for Asio operations diff --git a/doc/modules/ROOT/pages/8.examples/8p.asio-use-capy.adoc b/doc/modules/ROOT/pages/8.examples/8p.asio-use-capy.adoc index d2ea31a30..6b13f0c5f 100644 --- a/doc/modules/ROOT/pages/8.examples/8p.asio-use-capy.adoc +++ b/doc/modules/ROOT/pages/8.examples/8p.asio-use-capy.adoc @@ -5,7 +5,7 @@ Using Boost.Asio async operations directly inside a Capy coroutine through a `us == What This Example Shows -* How a completion token adapts _any_ Asio async operation into an xref:4.coroutines/4e.io-awaitable.adoc[IoAwaitable] +* How a completion token adapts _any_ Asio async operation into an xref:4.coroutines/4e.context-awaitable.adoc[ContextAwaitable] * How the token bridges `std::stop_token` to Asio's cancellation slot * How the bridge preserves the xref:4.coroutines/4d.executors.adoc#the-same-executor-invariant[same-executor invariant] @@ -16,7 +16,7 @@ This example is built when a `Boost::asio` target is available. The full source == The Completion Token -Asio async operations accept a _completion token_ that decides what the call returns. `use_capy` is a token whose `async_result` returns an cpp:IoAwaitable[]. When co-awaited, its `await_suspend` starts the Asio operation and arranges the completion handler to post the resumption through the caller's executor. The coroutine therefore resumes on the executor it was started with, never on whatever thread Asio completed on. From `example/asio/api/use_capy.hpp`: +Asio async operations accept a _completion token_ that decides what the call returns. `use_capy` is a token whose `async_result` returns a cpp:ContextAwaitable[]. When co-awaited, its `await_suspend` starts the Asio operation and arranges the completion handler to post the resumption through the caller's executor. The coroutine therefore resumes on the executor it was started with, never on whatever thread Asio completed on. From `example/asio/api/use_capy.hpp`: [source,cpp] ---- @@ -25,7 +25,7 @@ include::example$asio/api/use_capy.hpp[tag=await_suspend,indent=0] == Using the Token -Pass `use_capy` where an Asio operation expects a completion token. The `co_await` yields an cpp:io_result[] carrying the error code and the operation's result: +Pass `use_capy` where an Asio operation expects a completion token. The `co_await` yields a cpp:results[] carrying the error code and the operation's result: [source,cpp] ---- @@ -48,5 +48,5 @@ example complete! == See Also -* xref:4.coroutines/4e.io-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] — the general pattern +* xref:4.coroutines/4e.context-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] — the general pattern * xref:8.examples/8o.sender-bridge.adoc[Bridging a P2300 Sender] — the same technique for `std::execution` senders diff --git a/doc/modules/ROOT/pages/8.examples/8q.gui-integration.adoc b/doc/modules/ROOT/pages/8.examples/8q.gui-integration.adoc index 932d10bfb..024c522a8 100644 --- a/doc/modules/ROOT/pages/8.examples/8q.gui-integration.adoc +++ b/doc/modules/ROOT/pages/8.examples/8q.gui-integration.adoc @@ -103,7 +103,7 @@ cpp:run[] is what moves the work. It starts the inner task on the executor you n Three contracts combine to put that boundary on the GUI thread: -* cpp:run_async[] builds one cpp:io_env[] holding the executor it was given, and passes the task a pointer to it. +* cpp:run_async[] builds one cpp:await_context[] holding the executor it was given, and passes the task a pointer to it. * cpp:task[] propagates that pointer into every `co_await` in its body. A task completes by symmetric transfer and never posts, so nothing in the chain substitutes a different executor. * cpp:run[] records the caller's executor and posts the caller back through it when the inner task completes. That post runs on a pool thread and lands in the GUI queue. @@ -115,7 +115,7 @@ Two conditions carry the invariant, and both are contracts rather than magic. The executor must resume handles on one thread only. A GUI loop does, by construction. -Every awaitable in the chain must honor the cpp:IoAwaitable[] requirement to resume through cpp:io_env::executor[env->executor]. Capy's own types do: cpp:when_all[] and cpp:when_any[] return control to the caller through the caller's executor, and cpp:run[] does the same at an executor boundary. An awaitable that resumes the handle directly from a foreign thread breaks the invariant, and no other part of the library can repair it. <> shows one that honors it. +Every awaitable in the chain must honor the cpp:ContextAwaitable[] requirement to resume through cpp:await_context::executor[env->executor]. Capy's own types do: cpp:when_all[] and cpp:when_any[] return control to the caller through the caller's executor, and cpp:run[] does the same at an executor boundary. An awaitable that resumes the handle directly from a foreign thread breaks the invariant, and no other part of the library can repair it. <> shows one that honors it. ==== [#toolkit-completion] @@ -128,14 +128,14 @@ cpp:run[] covers work you hand to Capy. A toolkit also completes operations of i include::example$gui-integration/gui_integration.cpp[tag=dialog] ---- -Awaiting that needs an cpp:IoAwaitable[]. xref:4.coroutines/4e.io-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] covers the protocol. One line of it decides thread affinity: +Awaiting that needs a cpp:ContextAwaitable[]. xref:4.coroutines/4e.context-awaitable.adoc#bridging-a-foreign-awaitable[Bridging a Foreign Awaitable] covers the protocol. One line of it decides thread affinity: [source,cpp] ---- include::example$gui-integration/gui_integration.cpp[tag=dialog_awaitable] ---- -The toolkit's thread does not resume the coroutine. It posts the continuation through cpp:io_env::executor[env->executor], and the executor resumes it on the GUI thread. That is the second condition above, honored in one call. +The toolkit's thread does not resume the coroutine. It posts the continuation through cpp:await_context::executor[env->executor], and the executor resumes it on the GUI thread. That is the second condition above, honored in one call. The widget update after this `co_await` carries the same thread check as every other access. Resume the handle directly instead of posting, and the check fires. diff --git a/doc/modules/ROOT/pages/9.design/9b.Separation.adoc b/doc/modules/ROOT/pages/9.design/9b.Separation.adoc index 57197aa50..033d876cc 100644 --- a/doc/modules/ROOT/pages/9.design/9b.Separation.adoc +++ b/doc/modules/ROOT/pages/9.design/9b.Separation.adoc @@ -17,7 +17,7 @@ The answer is physical design. Capy and Corosio sit at different levels of the p == What Lives Where -**Capy** provides the foundational abstractions for coroutine-based I/O. Tasks. Buffers. Stream concepts. Executors. The IoAwaitable protocol. Type-erased streams. Composition primitives like cpp:when_all[] and cpp:when_any[]. It is pure {cpp}20. It has no platform-specific I/O code. No sockets. No file descriptors. No OS event loop. +**Capy** provides the foundational abstractions for coroutine-based I/O. Tasks. Buffers. Stream concepts. Executors. The ContextAwaitable protocol. Type-erased streams. Composition primitives like cpp:when_all[] and cpp:when_any[]. It is pure {cpp}20. It has no platform-specific I/O code. No sockets. No file descriptors. No OS event loop. **Corosio** provides platform networking. TCP sockets. TLS streams. DNS resolution. Delays and timeouts. Signal handling. It implements five platform-specific event loop backends: epoll and io_uring on Linux, kqueue on macOS/BSD, IOCP on Windows, and POSIX select as a fallback. Corosio depends on Capy. Capy does not depend on Corosio. @@ -148,7 +148,7 @@ Merging these into Capy would mean that a developer who wants a cpp:task[task<>] == Conclusion -Good design separates things that change for different reasons. Capy changes when the coroutine execution model evolves - new composition primitives, new buffer types, refinements to the IoAwaitable protocol. Corosio changes when platform I/O APIs evolve - new io_uring features on Linux, new IOCP capabilities on Windows, new TLS backends. +Good design separates things that change for different reasons. Capy changes when the coroutine execution model evolves - new composition primitives, new buffer types, refinements to the ContextAwaitable protocol. Corosio changes when platform I/O APIs evolve - new io_uring features on Linux, new IOCP capabilities on Windows, new TLS backends. The converse is also important: things that change together should not be separated. An unstable implementation detail that serves only one component belongs inside that component, not in a separate library. Capy and Corosio do not change together. diff --git a/doc/modules/ROOT/pages/9.design/9i.TypeEraseAwaitable.adoc b/doc/modules/ROOT/pages/9.design/9i.TypeEraseAwaitable.adoc index 8281f947a..6642bd8d3 100644 --- a/doc/modules/ROOT/pages/9.design/9i.TypeEraseAwaitable.adoc +++ b/doc/modules/ROOT/pages/9.design/9i.TypeEraseAwaitable.adoc @@ -20,7 +20,7 @@ struct vtable void (*construct_awaitable)(...); // 8 bool (*await_ready)(void*); // 8 std::coroutine_handle<> (*await_suspend)(void*, ...); // 8 - io_result (*await_resume)(void*); // 8 + results (*await_resume)(void*); // 8 void (*destroy_awaitable)(void*); // 8 size_t awaitable_size; // 8 size_t awaitable_align; // 8 @@ -46,11 +46,11 @@ bool await_ready() { return vt_->await_ready(cached_awaitable_); // true → no suspend } -std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) { +std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { return vt_->await_suspend(cached_awaitable_, h, env); } -io_result await_resume() { +results await_resume() { auto r = vt_->await_resume(cached_awaitable_); vt_->destroy_awaitable(cached_awaitable_); awaitable_active_ = owns_ = false; @@ -73,7 +73,7 @@ bool await_ready() { return vt_->await_ready(cached_awaitable_); } -io_result await_resume() { +results await_resume() { if(!owns_) return {std::error_code(), 0}; // short-circuited auto r = vt_->await_resume(cached_awaitable_); @@ -89,8 +89,8 @@ When a wrapper exposes more than one async operation, it cannot embed a single s Two sub-cases arise, distinguished by whether the operations share an await-return type: -* *Same await-return type* (Boost.Http's `http::any_read_source`: `read_some` and `read` both await-return cpp:io_result[io_result]). One ops struct (`awaitable_ops`) serves both operations; both `construct_*_awaitable` pointers return the same layout. -* *Different await-return types* (Boost.Http's `http::any_buffer_source`: `pull` await-returns cpp:io_result[io_result>] while its synthesized `read_some`/`read` await-return `io_result`; `http::any_buffer_sink` and `http::any_write_sink` similarly mix `io_result<>`, `io_result`). These need more than one ops struct (e.g. `awaitable_ops` plus `read_awaitable_ops`/`write_awaitable_ops`/`eof_awaitable_ops`), and each `construct` returns the ops matching its result type. +* *Same await-return type* (Boost.Http's `http::any_read_source`: `read_some` and `read` both await-return cpp:results[results]). One ops struct (`awaitable_ops`) serves both operations; both `construct_*_awaitable` pointers return the same layout. +* *Different await-return types* (Boost.Http's `http::any_buffer_source`: `pull` await-returns cpp:results[results>] while its synthesized `read_some`/`read` await-return `results`; `http::any_buffer_sink` and `http::any_write_sink` similarly mix `results<>`, `results`). These need more than one ops struct (e.g. `awaitable_ops` plus `read_awaitable_ops`/`write_awaitable_ops`/`eof_awaitable_ops`), and each `construct` returns the ops matching its result type. Capy's own multi-operation wrapper avoids this problem entirely. cpp:any_stream[] exposes two operations, `read_some` and `write_some`, but does not build a shared per-construct ops table for them: it inherits from cpp:any_read_stream[] and cpp:any_write_stream[], composing two independent flat, single-operation vtables. Per-construct ops are needed only when multiple operations act on one shared inner source or sink object. @@ -102,7 +102,7 @@ struct awaitable_ops { bool (*await_ready)(void*); std::coroutine_handle<> (*await_suspend)(void*, ...); - io_result (*await_resume)(void*); + results (*await_resume)(void*); void (*destroy)(void*); }; @@ -126,14 +126,14 @@ bool await_ready() const noexcept { return buffers_.empty(); // short-circuit } -std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) { +std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { active_ops_ = vt_->construct_read_some_awaitable(source_, cached_awaitable_, buffers_); if(active_ops_->await_ready(cached_awaitable_)) return h; // immediate → resume caller return active_ops_->await_suspend(cached_awaitable_, h, env); } -io_result await_resume() { +results await_resume() { if(!active_ops_) return {std::error_code(), 0}; // short-circuited auto r = active_ops_->await_resume(cached_awaitable_); @@ -184,12 +184,12 @@ The flat layout keeps all per-awaitable function pointers adjacent to `construct | cpp:any_stream[] (`read_some` via cpp:any_read_stream[]; `write_some` via cpp:any_write_stream[] -- two independent flat vtables, no shared ops table) | Per-construct ops, single ops struct (multiple operations, all sharing one await-return type) -| Boost.Http's `http::any_read_source` (`read_some`, `read` -- both cpp:io_result[io_result]) +| Boost.Http's `http::any_read_source` (`read_some`, `read` -- both cpp:results[results]) | Per-construct ops, multiple ops structs (operations differ in await-return type) -| Boost.Http's `http::any_buffer_source` (`pull` → cpp:io_result[io_result]; synthesized `read_some`/`read` → `io_result`) + - `http::any_buffer_sink` (`commit`/`commit_eof` → `io_result<>`; `write_some`/`write` → `io_result`) + - `http::any_write_sink` (`write_some`/`write` → `io_result`; `write_eof()` → `io_result<>`) +| Boost.Http's `http::any_buffer_source` (`pull` → cpp:results[results]; synthesized `read_some`/`read` → `results`) + + `http::any_buffer_sink` (`commit`/`commit_eof` → `results<>`; `write_some`/`write` → `results`) + + `http::any_write_sink` (`write_some`/`write` → `results`; `write_eof()` → `results<>`) |=== == Why the Flat Layout Cannot Scale diff --git a/doc/modules/ROOT/pages/9.design/9l.RunApi.adoc b/doc/modules/ROOT/pages/9.design/9l.RunApi.adoc index 9a8bdb152..a3e432492 100644 --- a/doc/modules/ROOT/pages/9.design/9l.RunApi.adoc +++ b/doc/modules/ROOT/pages/9.design/9l.RunApi.adoc @@ -9,7 +9,7 @@ Capy has two launcher functions: cpp:run_async[] (fire-and-forget from non-corou === `run_async` -- Fire-and-Forget -cpp:run_async[] starts any _IoRunnable_ from non-coroutine code: `main()`, callback handlers, event loops. cpp:task[task] is the most common conforming type, but any user-defined type satisfying the concept works. The function does not return a value to the caller. Handlers receive the task's result or exception after completion, as data; they should not throw. An exception that no handler consumes (none was supplied, or a handler let one escape) calls `std::terminate`; it is never silently discarded. To catch an error instead, `co_await` the work inside a coroutine. +cpp:run_async[] starts any _ContextRunnable_ from non-coroutine code: `main()`, callback handlers, event loops. cpp:task[task] is the most common conforming type, but any user-defined type satisfying the concept works. The function does not return a value to the caller. Handlers receive the task's result or exception after completion, as data; they should not throw. An exception that no handler consumes (none was supplied, or a handler let one escape) calls `std::terminate`; it is never silently discarded. To catch an error instead, `co_await` the work inside a coroutine. [source,cpp] ---- @@ -18,7 +18,7 @@ include::example$snippets/9l_run_api.cpp[tag=run_async_usage,indent=0] === `run` -- Awaitable Within a Coroutine -cpp:run[] is the coroutine-side counterpart. It binds any _IoRunnable_ to a (possibly different) executor and returns the result to the caller via `co_await`. It also supports overloads that customize stop token or allocator while inheriting the caller's executor. +cpp:run[] is the coroutine-side counterpart. It binds any _ContextRunnable_ to a (possibly different) executor and returns the result to the caller via `co_await`. It also supports overloads that customize stop token or allocator while inheriting the caller's executor. [source,cpp] ---- @@ -264,7 +264,7 @@ auto op = connect(sndr, receiver); // Allocator available NOW -- too late start(op); ---- -In the _IoAwaitable_ model, context flows _forward_ from launcher to task: +In the _ContextAwaitable_ model, context flows _forward_ from launcher to task: [role=figure] ---- diff --git a/doc/modules/ROOT/pages/9.design/9m.WhyNotAsio.adoc b/doc/modules/ROOT/pages/9.design/9m.WhyNotAsio.adoc index 09a66dc51..5d57ef8cb 100644 --- a/doc/modules/ROOT/pages/9.design/9m.WhyNotAsio.adoc +++ b/doc/modules/ROOT/pages/9.design/9m.WhyNotAsio.adoc @@ -134,7 +134,7 @@ The standard has `std::execution` (P2300), the sender/receiver model. It's power Capy builds an execution model purpose-built for coroutines and I/O. -The *IoAwaitable protocol* solves context propagation. When you `co_await`, the caller passes its executor and stop token to the child through an extended `await_suspend` signature. No explicit parameters. No promise coupling. Context flows forward, naturally. +The *ContextAwaitable protocol* solves context propagation. When you `co_await`, the caller passes its executor and stop token to the child through an extended `await_suspend` signature. No explicit parameters. No promise coupling. Context flows forward, naturally. *Stop tokens propagate automatically.* Cancel at the top of your coroutine tree, and every nested operation receives the signal. Capy integrates with OS-level cancellation—`CancelIoEx` on Windows, `IORING_OP_ASYNC_CANCEL` on Linux. Pending I/O operations cancel immediately. @@ -142,13 +142,13 @@ The *IoAwaitable protocol* solves context propagation. When you `co_await`, the And Capy *separates execution from platform*. The execution model—executors, cancellation, allocation—lives in Capy. Platform abstractions—sockets, `io_uring`, IOCP—live in Corosio. Clean boundaries. Testable components. You can use Capy's execution model with a different I/O backend if you choose. -Most importantly, Capy defines a *taxonomy of awaitables*. cpp:IoAwaitable[] is the base protocol for any type that participates in context propagation. cpp:IoRunnable[] refines it with the interface that cpp:run_async[] and cpp:run[] need to start a task. This hierarchy means you can write your own task types that integrate with Capy's execution model. Asio's `awaitable` is a concrete type, not a concept. You use it or you don't. Capy gives you building blocks. +Most importantly, Capy defines a *taxonomy of awaitables*. cpp:ContextAwaitable[] is the base protocol for any type that participates in context propagation. cpp:ContextRunnable[] refines it with the interface that cpp:run_async[] and cpp:run[] need to start a task. This hierarchy means you can write your own task types that integrate with Capy's execution model. Asio's `awaitable` is a concrete type, not a concept. You use it or you don't. Capy gives you building blocks. Neither Asio nor `std::execution` offers this combination of forward-flow allocator control, automatic stop-token propagation, and execution/platform separation. === What Capy Offers -* cpp:IoAwaitable[], cpp:IoRunnable[] — taxonomy of awaitable concepts +* cpp:ContextAwaitable[], cpp:ContextRunnable[] — taxonomy of awaitable concepts * cpp:task[task] — concrete task type implementing the protocol (user-defined tasks also supported) * cpp:run[], cpp:run_async[] — launcher functions with forward-flow allocator control * cpp:strand[], cpp:thread_pool[], cpp:async_mutex[], cpp:async_event[], cpp:async_waker[]: concurrency primitives @@ -162,15 +162,15 @@ cpp:strand[], cpp:thread_pool[] and cpp:execution_context[] carry the same names |=== | Capy | Asio | std -| cpp:IoAwaitable[] +| cpp:ContextAwaitable[] ^| - ^| - -| cpp:IoRunnable[] +| cpp:ContextRunnable[] ^| - ^| - -| cpp:io_awaitable_promise_base[] +| cpp:context_awaitable_promise_base[] ^| - ^| - diff --git a/doc/modules/ROOT/pages/9.design/9n.WhyNotCobalt.adoc b/doc/modules/ROOT/pages/9.design/9n.WhyNotCobalt.adoc index c26e81cb2..cfe420328 100644 --- a/doc/modules/ROOT/pages/9.design/9n.WhyNotCobalt.adoc +++ b/doc/modules/ROOT/pages/9.design/9n.WhyNotCobalt.adoc @@ -218,14 +218,14 @@ Capy supports multi-threaded execution. cpp:thread_pool[] distributes work acros Cobalt stores executor context in thread-local variables. Coroutines access it via `this_coro::executor`. This design is scoped to single-threaded, single-executor configurations. -Capy introduces the https://wg21.link/P4003[IoAwaitable protocol] and uses it for context propagation. When you `co_await`, the caller passes its execution environment to the child structurally: +Capy introduces the https://wg21.link/P4003[ContextAwaitable protocol] and uses it for context propagation. When you `co_await`, the caller passes its execution environment to the child structurally: [source,cpp] ---- include::example$snippets/9n_why_not_cobalt.cpp[tag=await_suspend_env,indent=0] ---- -No thread-local state. No ambient context. The executor and stop token flow forward through the call chain via the cpp:io_env[] parameter. +No thread-local state. No ambient context. The executor and stop token flow forward through the call chain via the cpp:await_context[] parameter. [cols="1,1,1"] |=== @@ -244,7 +244,7 @@ No thread-local state. No ambient context. The executor and stop token flow forw | No | Stop token delivery -| Structural (cpp:io_env[]) +| Structural (cpp:await_context[]) | `this_coro::cancellation_state` |=== @@ -252,7 +252,7 @@ No thread-local state. No ambient context. The executor and stop token flow forw Both libraries propagate cancellation automatically through coroutine chains. Both support OS-level cancellation of pending I/O operations (`CancelIoEx` on Windows, `IORING_OP_ASYNC_CANCEL` on Linux). -Capy uses `std::stop_token`, propagated via the IoAwaitable protocol's cpp:io_env[] parameter. The token flows forward structurally alongside the executor. +Capy uses `std::stop_token`, propagated via the ContextAwaitable protocol's cpp:await_context[] parameter. The token flows forward structurally alongside the executor. Cobalt uses Asio's `cancellation_signal` and `cancellation_slot`. Propagation is wired automatically in `await_suspend` via `forward_cancellation`. `this_coro::cancellation_state` provides filtering control over which cancellation types pass through. @@ -265,7 +265,7 @@ Cobalt uses Asio's `cancellation_signal` and `cancellation_slot`. Propagation is | `asio::cancellation_signal` | Propagation -| Automatic (cpp:io_env[]) +| Automatic (cpp:await_context[]) | Automatic (slot/signal wiring) | Filtering @@ -361,7 +361,7 @@ cpp:recycling_memory_resource[] provides zero-overhead recycling after warmup. M Cobalt is coupled to Asio's `io_context`. The execution model and the platform abstractions are one thing. -Capy separates them. The execution model — executors, cancellation, allocation — lives in Capy. Platform abstractions live in Corosio, a companion library that provides native TCP sockets, acceptors, TLS streams, timers, DNS resolution, and signal handling. It builds on Capy's IoAwaitable protocol with five native backends: epoll and io_uring (Linux), kqueue (BSD/macOS), IOCP (Windows), and select (portable fallback). You can test Capy's execution model without a network stack. You can swap the I/O backend without changing your application code. +Capy separates them. The execution model — executors, cancellation, allocation — lives in Capy. Platform abstractions live in Corosio, a companion library that provides native TCP sockets, acceptors, TLS streams, timers, DNS resolution, and signal handling. It builds on Capy's ContextAwaitable protocol with five native backends: epoll and io_uring (Linux), kqueue (BSD/macOS), IOCP (Windows), and select (portable fallback). You can test Capy's execution model without a network stack. You can swap the I/O backend without changing your application code. [cols="1,1,1"] |=== diff --git a/doc/modules/ROOT/pages/9.design/9o.WhyNotCobaltConcepts.adoc b/doc/modules/ROOT/pages/9.design/9o.WhyNotCobaltConcepts.adoc index b578e0dba..6051a7001 100644 --- a/doc/modules/ROOT/pages/9.design/9o.WhyNotCobaltConcepts.adoc +++ b/doc/modules/ROOT/pages/9.design/9o.WhyNotCobaltConcepts.adoc @@ -22,29 +22,29 @@ Capy formally defines what makes a task type conforming. Two {cpp}20 concepts fo [role=figure] .... -IoAwaitable +ContextAwaitable | v -IoRunnable +ContextRunnable .... -cpp:IoAwaitable[] is the base. It describes what operations a type must provide +cpp:ContextAwaitable[] is the base. It describes what operations a type must provide to be able to be ``co_await``ed inside a Capy-coroutine: * `await_ready` -- tells if the coroutine needs to be suspended. * `await_suspend` -- invoked immediately after the coroutine is suspended. - Obtains the execution environment via an cpp:io_env[] parameter and tells which coroutine needs to be now resumed. + Obtains the execution environment via an cpp:await_context[] parameter and tells which coroutine needs to be now resumed. * `await_resume` -- tells what value shall be returned when the coroutine is resumed. -cpp:IoRunnable[] refines cpp:IoAwaitable[] with operations needed to start a task from non-coroutine contexts: cpp:IoRunnable::handle[handle()], cpp:IoRunnable::release[release()], cpp:IoRunnable::exception[exception()], cpp:IoRunnable::result[result()], and the promise-level cpp:IoRunnable::set_continuation[set_continuation()] and cpp:IoRunnable::set_environment[set_environment()]. +cpp:ContextRunnable[] refines cpp:ContextAwaitable[] with operations needed to start a task from non-coroutine contexts: cpp:ContextRunnable::handle[handle()], cpp:ContextRunnable::release[release()], cpp:ContextRunnable::exception[exception()], cpp:ContextRunnable::result[result()], and the promise-level cpp:ContextRunnable::set_continuation[set_continuation()] and cpp:ContextRunnable::set_environment[set_environment()]. [source,cpp] ---- -include::example$include/boost/capy/concept/io_runnable.hpp[tag=io_runnable_concept] +include::example$include/boost/capy/concept/context_runnable.hpp[tag=context_runnable_concept] ---- -Context injection (cpp:IoRunnable::set_environment[set_environment], cpp:IoRunnable::set_continuation[set_continuation]) consists of `noexcept` requirements that cpp:IoRunnable[] places on `T::promise_type`; launcher functions invoke them through the typed handle returned by cpp:IoRunnable::handle[handle()] before resuming the frame. +Context injection (cpp:ContextRunnable::set_environment[set_environment], cpp:ContextRunnable::set_continuation[set_continuation]) consists of `noexcept` requirements that cpp:ContextRunnable[] places on `T::promise_type`; launcher functions invoke them through the typed handle returned by cpp:ContextRunnable::handle[handle()] before resuming the frame. Each concept has documented syntactic requirements, semantic requirements, conforming signatures, and examples. A user who wants to create a custom task type can read the concept definition and know exactly what to provide. The compiler enforces the syntactic requirements at constraint-check time. @@ -93,7 +93,7 @@ Each awaitable probes for the associators independently using `if constexpr`. Th | Aspect | Capy | Cobalt | Task requirements -| Named concepts (cpp:IoAwaitable[], cpp:IoRunnable[]) +| Named concepts (cpp:ContextAwaitable[], cpp:ContextRunnable[]) | Implicit associators probed via `if constexpr` | Specification @@ -117,14 +117,14 @@ Each awaitable probes for the associators independently using `if constexpr`. Th The task requirements directly determine how executor and cancellation context reach child operations. -Capy's cpp:IoAwaitable[] protocol passes the execution environment as an explicit parameter to `await_suspend`: +Capy's cpp:ContextAwaitable[] protocol passes the execution environment as an explicit parameter to `await_suspend`: [source,cpp] ---- include::example$snippets/9o_why_not_cobalt_concepts.cpp[tag=await_suspend_env,indent=0] ---- -The executor and stop token flow forward structurally through the call chain via cpp:io_env[]. If a task's machinery does not provide them, the code does not compile. There is no fallback. +The executor and stop token flow forward structurally through the call chain via cpp:await_context[]. If a task's machinery does not provide them, the code does not compile. There is no fallback. Cobalt's approach probes the calling promise. The relevant code from `cobalt/detail/task.hpp`: @@ -477,7 +477,7 @@ This separation is the architectural root of the differences examined in this do | Design Choice | Capy | Cobalt | Task requirements -| Named concept hierarchy (cpp:IoAwaitable[] -> cpp:IoRunnable[]) +| Named concept hierarchy (cpp:ContextAwaitable[] -> cpp:ContextRunnable[]) | Implicit associators (`get_executor`, `get_cancellation_slot`, `get_allocator`) | Context propagation diff --git a/doc/modules/ROOT/pages/9.design/9p.WhyNotTMC.adoc b/doc/modules/ROOT/pages/9.design/9p.WhyNotTMC.adoc index 2faae15f3..1e3d6364b 100644 --- a/doc/modules/ROOT/pages/9.design/9p.WhyNotTMC.adoc +++ b/doc/modules/ROOT/pages/9.design/9p.WhyNotTMC.adoc @@ -32,7 +32,7 @@ When async code finishes waiting, it needs to resume somewhere. Where? *Capy's answer:* The same place it started. Automatically. -* Context flows forward through `await_suspend(h, env)` where `env` is an `io_env` +* Context flows forward through `await_suspend(h, env)` where `env` is an `await_context` * Your coroutine started on executor X? It resumes on executor X. *TMC's answer:* Where you tell it, with flexibility to change mid-execution. @@ -152,7 +152,7 @@ include::example$snippets/9p_why_not_tmc.cpp[tag=arena,indent=0] For I/O workloads where HALO cannot apply, allocator control is essential, not optional. -== Getting Technical: The IoAwaitable Protocol +== Getting Technical: The ContextAwaitable Protocol When you write `co_await something`, what happens? @@ -176,9 +176,9 @@ The awaitable receives: * `h` — The handle (for resumption) * `env` — The execution environment containing: -** cpp:io_env::executor[env->executor] — The executor (where to resume) -** cpp:io_env::stop_token[env->stop_token] — A stop token (for cancellation) -** cpp:io_env::frame_allocator[env->frame_allocator] — An optional `std::pmr::memory_resource*` for coroutine frame allocation (null selects the default allocator) +** cpp:await_context::executor[env->executor] — The executor (where to resume) +** cpp:await_context::stop_token[env->stop_token] — A stop token (for cancellation) +** cpp:await_context::frame_allocator[env->frame_allocator] — An optional `std::pmr::memory_resource*` for coroutine frame allocation (null selects the default allocator) *TMC's approach:* @@ -359,7 +359,7 @@ Corosio builds real networking on Capy: * Timers, DNS resolution, signal handling * Native backends: epoll and io_uring (Linux), kqueue (BSD/macOS), IOCP (Windows), select (portable fallback) -All built on Capy's IoAwaitable protocol. Coroutines only. No callbacks. +All built on Capy's ContextAwaitable protocol. Coroutines only. No callbacks. == When to Use Each diff --git a/doc/modules/ROOT/pages/A.specification-methods/A.intro.adoc b/doc/modules/ROOT/pages/A.specification-methods/A.intro.adoc index d30d64ec5..608d6cf74 100644 --- a/doc/modules/ROOT/pages/A.specification-methods/A.intro.adoc +++ b/doc/modules/ROOT/pages/A.specification-methods/A.intro.adoc @@ -16,7 +16,7 @@ following xref:reference:boost/capy.adoc[Reference] section. == What This Section Covers * xref:A.specification-methods/Ab.cancellation.adoc[Cancellation] -- What it means for a - function to support `IoAwaitable` cancellation via a propagated `std::stop_token`. + function to support `ContextAwaitable` cancellation via a propagated `std::stop_token`. * xref:A.specification-methods/Ac.contingencies.adoc[Contingencies] -- What a _contingency_ is, and how the reference describes operations that cannot complete in full. diff --git a/doc/modules/ROOT/pages/A.specification-methods/Ab.cancellation.adoc b/doc/modules/ROOT/pages/A.specification-methods/Ab.cancellation.adoc index 7d52272e2..410249760 100644 --- a/doc/modules/ROOT/pages/A.specification-methods/Ab.cancellation.adoc +++ b/doc/modules/ROOT/pages/A.specification-methods/Ab.cancellation.adoc @@ -10,11 +10,11 @@ = Cancellation :page-mode: explanation -A function is said to _support IoAwaitable cancellation_ when its return type -models concept cpp:IoAwaitable[] and this return object `a` controls a coroutine which +A function is said to _support ContextAwaitable cancellation_ when its return type +models concept cpp:ContextAwaitable[] and this return object `a` controls a coroutine which can be prematurely stopped using the `std::stop_token` propagated through the -`IoAwaitable` protocol. Additionally, if the result type of expression `co_await a` -in the context of a Capy-coroutine is a specialization of cpp:io_result[] +`ContextAwaitable` protocol. Additionally, if the result type of expression `co_await a` +in the context of a Capy-coroutine is a specialization of cpp:results[] then the cancelling of an operation is considered a contingency represented by condition cpp:cond::canceled[cond::canceled]. diff --git a/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc b/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc index 4f02dc14e..45acc276b 100644 --- a/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc +++ b/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc @@ -20,7 +20,7 @@ as their postconditions never say that the operation indeed processes the reques number of bytes. Each stream operation that may encounter a contingency await-returns -a type which is a specialization of cpp:capy::io_result[capy::io_result]. These objects can be _destructured_ +a type which is a specialization of cpp:capy::results[capy::results]. These objects can be _destructured_ using a structured binding. The first binding of such destructuring is of type `std::error_code`. This binding, call it `ec`, is used to signal if and which contingency occurred: @@ -38,7 +38,7 @@ NOTE: The stream operations can still throw exceptions to indicate conditions, their postconditions, such as failures to grow a buffer, or failure to allocate a coroutine frame. -NOTE: Operations on streams often await-return cpp:capy::io_result[capy::io_result] +NOTE: Operations on streams often await-return cpp:capy::results[capy::results] destructuring to `[ec, n]`, where `n` represents the number of processed bytes. Upon a reported contingency, a non-zero `n` indicates the state of the partial or sometimes even a full read. When an inner operation reports a contingency, diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 42ebc1d0a..c0d7854d5 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -17,7 +17,7 @@ It offers a number of task synchronization mechanisms, ranging from low-level, l to high-level, like _strands_, `when_all`/`when_any` "joins", and the resume-on-the-same-executor guarantee. Libraries that want to provide concrete I/O implementations -- like those based on `epoll` or `io_uring` -- -can plug into Capy's system via the `IoAwaitable` protocol, which describes how tasks can be scheduled, +can plug into Capy's system via the `ContextAwaitable` protocol, which describes how tasks can be scheduled, cancelled, and allocated in memory. You also get the testing tools that allow you to test your asynchronous flows in a deterministic way. @@ -48,6 +48,6 @@ include::example$programs/index_page_echo.cpp[tag=full] * xref:quick-start.adoc[Quick Start] — Set up your first Capy project * xref:2.cpp20-coroutines/2a.foundations.adoc[{cpp}20 Coroutines Tutorial] — Learn coroutines from the ground up -* xref:4.coroutines/4b.tasks.adoc[Coroutines in Capy] — Deep dive into cpp:task[task] and the IoAwaitable protocol +* xref:4.coroutines/4b.tasks.adoc[Coroutines in Capy] — Deep dive into cpp:task[task] and the ContextAwaitable protocol * xref:5.buffers/5.intro.adoc[Buffer Sequences] — Contiguous buffers, composite buffers, system I/O, and the algorithms over them * xref:6.streams/6a.concepts.adoc[Stream Concepts] — Understand the three stream concepts diff --git a/doc/quitter.md b/doc/quitter.md index 4a1a1414f..3a4130a4c 100644 --- a/doc/quitter.md +++ b/doc/quitter.md @@ -2,7 +2,7 @@ ## Summary -`quitter` is a task type that satisfies `IoRunnable` and provides +`quitter` is a task type that satisfies `ContextRunnable` and provides transparent cancellation. When the stop token is triggered, the coroutine body never sees the cancellation. The promise intercepts it on resume and short-circuits to `final_suspend`. RAII cleanup @@ -20,7 +20,7 @@ achieves this via `set_stopped` - the coroutine never resumes. Capy's handling. `quitter` closes this gap. It is a policy-level task type: same -IoAwaitable protocol, same io_env propagation, same frame allocator, +ContextAwaitable protocol, same await_context propagation, same frame allocator, same symmetric transfer - but with automatic stop-on-resume behavior. ## Behavior @@ -32,7 +32,7 @@ returns a value or throws. No observable difference. ### When stop is requested -1. The stop token in `io_env` is triggered (from a parent, a scope, +1. The stop token in `await_context` is triggered (from a parent, a scope, or application shutdown). 2. The in-flight I/O operation observes the token and completes (typically with `operation_aborted`). @@ -79,14 +79,14 @@ struct [[nodiscard]] quitter { struct promise_type; - // IoAwaitable + // ContextAwaitable bool await_ready() const noexcept; auto await_resume(); std::coroutine_handle<> await_suspend( std::coroutine_handle<> cont, - io_env const* env); + await_context const* env); - // IoRunnable + // ContextRunnable std::coroutine_handle handle() const noexcept; void release() noexcept; @@ -104,7 +104,7 @@ struct [[nodiscard]] quitter ```cpp struct quitter::promise_type - : io_awaitable_promise_base + : context_awaitable_promise_base , detail::task_return_base { // Completion state @@ -253,7 +253,7 @@ Identical to `task::await_suspend()`: ```cpp std::coroutine_handle<> await_suspend( std::coroutine_handle<> cont, - io_env const* env) + await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(env); @@ -272,13 +272,13 @@ destructor - all identical to `task`. `quitter` satisfies: -- `IoAwaitable` - has `await_suspend(coroutine_handle<>, io_env const*)` -- `IoRunnable` - has `promise_type`, `handle()`, `release()`, +- `ContextAwaitable` - has `await_suspend(coroutine_handle<>, await_context const*)` +- `ContextRunnable` - has `promise_type`, `handle()`, `release()`, `exception()`, `set_continuation()`, `set_environment()`, `result()` (non-void) `quitter` is usable with `run`, `run_async`, `when_all`, -`when_any`, and any launcher that accepts `IoRunnable`. +`when_any`, and any launcher that accepts `ContextRunnable`. ## Interaction with `when_all` / `when_any` @@ -290,7 +290,7 @@ cleanly. The parent combinator sees the stopped completions and propagates accordingly. No changes to `when_all` or `when_any` are needed. The stop -propagation already works through `io_env`. The only new behavior +propagation already works through `await_context`. The only new behavior is inside `quitter`'s `transform_awaiter::await_resume()`. ## Usage @@ -432,7 +432,7 @@ explicit error handling. The task type is a policy point. Users who want explicit error handling use `task`. Users who want transparent cancellation -use `quitter`. The IoAwaitable protocol does not mandate a +use `quitter`. The ContextAwaitable protocol does not mandate a task. This is the design P4003 enables. **Why throw internally instead of `goto final_suspend`?** diff --git a/doc/read-some-rationale.md b/doc/read-some-rationale.md index 36399f800..6831cc0f9 100644 --- a/doc/read-some-rationale.md +++ b/doc/read-some-rationale.md @@ -58,7 +58,7 @@ template< typename T > concept ReadStream = requires( T& stream, mutable_buffer_archetype buffers ) { - { stream.read_some( buffers ) } -> IoAwaitable; + { stream.read_some( buffers ) } -> ContextAwaitable; requires awaitable_decomposes_to< decltype( stream.read_some( buffers ) ), std::error_code, std::size_t >; @@ -104,7 +104,7 @@ template< typename T > concept WriteStream = requires( T& stream, const_buffer_archetype buffers ) { - { stream.write_some( buffers ) } -> IoAwaitable; + { stream.write_some( buffers ) } -> ContextAwaitable; requires awaitable_decomposes_to< decltype( stream.write_some( buffers ) ), std::error_code, std::size_t >; diff --git a/doc/sys-exec-rationale.md b/doc/sys-exec-rationale.md index 5808aaf55..39379ffcc 100644 --- a/doc/sys-exec-rationale.md +++ b/doc/sys-exec-rationale.md @@ -89,13 +89,13 @@ run_async( ioc.get_executor(), tp.get_executor() )( my_task() ); at a point where they may lack the contextual information to choose well. -### Position C: Propagate via `io_env` (the Coroutine Environment) +### Position C: Propagate via `await_context` (the Coroutine Environment) Add a second executor slot to the coroutine environment that is propagated implicitly, like the I/O executor already is: ```cpp -struct io_env { +struct await_context { executor_ref executor; executor_ref compute_executor; // new std::stop_token stop_token; @@ -126,21 +126,21 @@ co_await run( ex )( bcrypt() ); **Arguments against:** -1. Sets a precedent for adding cross-cutting concerns to `io_env`. +1. Sets a precedent for adding cross-cutting concerns to `await_context`. If we add a compute executor, why not a logger? An allocator? A TLS context? Each addition increases the overhead of every `run` and `run_async` call. -2. The `IoAwaitable` concept must grow to accommodate each new - `io_env` member. +2. The `ContextAwaitable` concept must grow to accommodate each new + `await_context` member. 3. It is unclear what the default should be when the user does not set the compute executor (see below). -### Position D: Extensible `io_env` via Type-Erased Storage +### Position D: Extensible `await_context` via Type-Erased Storage Add a general-purpose extensibility mechanism rather than named fields: ```cpp -struct io_env { +struct await_context { executor_ref executor; std::stop_token stop_token; std::pmr::memory_resource* frame_allocator = nullptr; @@ -164,7 +164,7 @@ struct io_env { ## The Default Value Problem -If the compute executor lives in `io_env`, what happens when the user +If the compute executor lives in `await_context`, what happens when the user does not set it? | Option | Behavior | Trade-off | @@ -203,10 +203,10 @@ There is no consensus on the right default. non-standard library (where it can be removed after experience proves it harmful) but must not enter the standard (where removal is effectively impossible). -2. **`io_env` propagation is the least-bad option** *if* we accept +2. **`await_context` propagation is the least-bad option** *if* we accept that explicit parameter threading does not scale. Both sides acknowledge this is more principled than a global, though one - side worries about `io_env` scope creep. + side worries about `await_context` scope creep. 3. **Practical servers almost always need a compute pool.** This weakens the "propagated by all, used by none" objection but does not eliminate it. @@ -222,22 +222,22 @@ There is no consensus on the right default. both but document the global as the convenience fallback. The other says the global's existence will undermine the propagation mechanism because developers will always take the easier path. -3. **Whether adding a second executor to `io_env` is acceptable +3. **Whether adding a second executor to `await_context` is acceptable scope.** One position views it as the principled solution. The other views it as the first step on a slippery slope of ever-growing coroutine environment state. ## Summary of Alternatives -| # | Approach | Global? | Propagation | `io_env` growth | User burden | +| # | Approach | Global? | Propagation | `await_context` growth | User burden | | --- | ------------------------------- | ------- | ------------ | --------------- | -------------------- | | A | Global system executor | Yes | N/A | None | Minimal | | B | Explicit parameter threading | No | Manual | None | High (every call) | -| C | Named slot in `io_env` | No | Automatic | +1 field | Low (at launch site) | -| D | Extensible `io_env` (polystore) | No | Automatic | Unbounded | Low (at launch site) | -| A+C | Global + `io_env` slot | Yes | Automatic | +1 field | Minimal | +| C | Named slot in `await_context` | No | Automatic | +1 field | Low (at launch site) | +| D | Extensible `await_context` (polystore) | No | Automatic | Unbounded | Low (at launch site) | +| A+C | Global + `await_context` slot | Yes | Automatic | +1 field | Minimal | The design decision hinges on which cost the committee finds least acceptable: the existence of a global singleton that cannot be removed, the ergonomic burden of manual propagation, or the precedent of -expanding `io_env` with cross-cutting concerns. +expanding `await_context` with cross-cutting concerns. diff --git a/doc/unlisted/io-awaitables-allocator.adoc b/doc/unlisted/context-awaitables-allocator.adoc similarity index 100% rename from doc/unlisted/io-awaitables-allocator.adoc rename to doc/unlisted/context-awaitables-allocator.adoc diff --git a/doc/unlisted/io-awaitables-concepts.adoc b/doc/unlisted/context-awaitables-concepts.adoc similarity index 82% rename from doc/unlisted/io-awaitables-concepts.adoc rename to doc/unlisted/context-awaitables-concepts.adoc index 39f883696..05f1f2d4e 100644 --- a/doc/unlisted/io-awaitables-concepts.adoc +++ b/doc/unlisted/context-awaitables-concepts.adoc @@ -37,11 +37,11 @@ synchronization everywhere. == The Solution: Extended await_suspend -Capy awaitables receive context via `io_env` in `await_suspend`: +Capy awaitables receive context via `await_context` in `await_suspend`: [source,cpp] ---- -auto await_suspend(std::coroutine_handle<> h, io_env const* env) +auto await_suspend(std::coroutine_handle<> h, await_context const* env) { // Now we know where to dispatch and can check cancellation if (env->stop_token.stop_requested()) @@ -62,46 +62,46 @@ The promise passes the execution environment to every awaitable through Capy defines two progressively capable concepts: ---- -IoAwaitable +ContextAwaitable │ ▼ -IoRunnable +ContextRunnable ---- -=== IoAwaitable +=== ContextAwaitable -The base concept. An `IoAwaitable` can receive context when awaited: +The base concept. A `ContextAwaitable` can receive context when awaited: [source,cpp] ---- template -concept IoAwaitable = requires(T a, std::coroutine_handle<> h, io_env const* env) +concept ContextAwaitable = requires(T a, std::coroutine_handle<> h, await_context const* env) { { a.await_ready() } -> std::convertible_to; - { a.await_suspend(h, env) }; // Two-argument form with io_env + { a.await_suspend(h, env) }; // Two-argument form with await_context a.await_resume(); }; ---- Key features: -* Two-argument `await_suspend` receives `io_env` containing executor and stop token +* Two-argument `await_suspend` receives `await_context` containing executor and stop token * Works with any executor type (via `executor_ref`) * Enables automatic context propagation -* Awaitables should store the `io_env` as a pointer (`io_env const*`), never a copy. Launch functions guarantee the `io_env` outlives the operation. +* Awaitables should store the `await_context` as a pointer (`await_context const*`), never a copy. Launch functions guarantee the `await_context` outlives the operation. -Reference: `` +Reference: `` -=== IoRunnable +=== ContextRunnable -An `IoRunnable` is an `IoAwaitable` that can be launched from non-coroutine +A `ContextRunnable` is a `ContextAwaitable` that can be launched from non-coroutine contexts by utilities like `run` and `run_async`: [source,cpp] ---- template -concept IoRunnable = - IoAwaitable && +concept ContextRunnable = + ContextAwaitable && requires { typename T::promise_type; } && requires(T& t, T const& ct, typename T::promise_type const& cp) { @@ -126,7 +126,7 @@ Context injection (`set_environment`, `set_continuation`) is handled internally by the promise through `await_suspend` and is not part of any concept. This is what `task` implements. -Reference: `` +Reference: `` == How Context Flows @@ -171,7 +171,7 @@ io_operation's await_suspend(h, ex, token) === Forward Propagation (Capy's Approach) -Context flows _forward_ through `await_suspend` via `io_env`: +Context flows _forward_ through `await_suspend` via `await_context`: [source,cpp] ---- @@ -209,13 +209,13 @@ Capy uses forward propagation for clarity and composability. == The Promise Support Mixin -To implement an `IoRunnable` task, use the `io_awaitable_promise_base` CRTP mixin: +To implement a `ContextRunnable` task, use the `context_awaitable_promise_base` CRTP mixin: [source,cpp] ---- struct my_task { - struct promise_type : io_awaitable_promise_base + struct promise_type : context_awaitable_promise_base { my_task get_return_object(); std::suspend_always initial_suspend(); @@ -276,13 +276,13 @@ Every `co_await` in a task automatically receives context. |=== | Concept | Capability -| `IoAwaitable` +| `ContextAwaitable` | Receives executor and stop token in `await_suspend` -| `IoRunnable` +| `ContextRunnable` | Adds launch interface (`handle()`, `release()`, `exception()`, `result()`) -| `io_awaitable_promise_base` +| `context_awaitable_promise_base` | CRTP mixin implementing context storage and propagation |=== diff --git a/doc/unlisted/io-awaitables-executor.adoc b/doc/unlisted/context-awaitables-executor.adoc similarity index 100% rename from doc/unlisted/io-awaitables-executor.adoc rename to doc/unlisted/context-awaitables-executor.adoc diff --git a/doc/unlisted/io-awaitables-launching.adoc b/doc/unlisted/context-awaitables-launching.adoc similarity index 94% rename from doc/unlisted/io-awaitables-launching.adoc rename to doc/unlisted/context-awaitables-launching.adoc index 6988ad2f7..b2e2c783f 100644 --- a/doc/unlisted/io-awaitables-launching.adoc +++ b/doc/unlisted/context-awaitables-launching.adoc @@ -261,24 +261,24 @@ task parent() == Lifetime Considerations -=== io_env Lifetime +=== await_context Lifetime -Launch functions (`run_async`, `run`) own the `io_env` that is passed to -every awaitable in the chain. The `io_env` is guaranteed to outlive the +Launch functions (`run_async`, `run`) own the `await_context` that is passed to +every awaitable in the chain. The `await_context` is guaranteed to outlive the task and all awaitables it contains. This is a responsibility of the launch function: -* `run_async` stores `io_env` in its trampoline coroutine frame, which +* `run_async` stores `await_context` in its trampoline coroutine frame, which outlives the launched task. -* `run` stores `io_env` in its awaitable, which persists for the entire +* `run` stores `await_context` in its awaitable, which persists for the entire `co_await` expression. -Because of this guarantee, awaitables that receive `io_env const*` in +Because of this guarantee, awaitables that receive `await_context const*` in `await_suspend` should store it directly; copying is unnecessary and wasteful. [source,cpp] ---- -std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) +std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { env_ = env; // Correct: store pointer // ... diff --git a/doc/unlisted/io-awaitables-stop-token.adoc b/doc/unlisted/context-awaitables-stop-token.adoc similarity index 98% rename from doc/unlisted/io-awaitables-stop-token.adoc rename to doc/unlisted/context-awaitables-stop-token.adoc index 2f08a4efc..7815c528e 100644 --- a/doc/unlisted/io-awaitables-stop-token.adoc +++ b/doc/unlisted/context-awaitables-stop-token.adoc @@ -169,7 +169,7 @@ struct stoppable_timer { std::chrono::milliseconds duration_; timer_handle handle_; - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; bool cancelled_ = false; bool await_ready() const @@ -177,7 +177,7 @@ struct stoppable_timer return duration_.count() <= 0; } - void await_suspend(std::coroutine_handle<> h, io_env const* env) + void await_suspend(std::coroutine_handle<> h, await_context const* env) { env_ = env; // Store pointer, never copy diff --git a/doc/unlisted/coroutines-when-all.adoc b/doc/unlisted/coroutines-when-all.adoc index 409aa849e..2b103ef9d 100644 --- a/doc/unlisted/coroutines-when-all.adoc +++ b/doc/unlisted/coroutines-when-all.adoc @@ -19,9 +19,9 @@ Tasks are sequential by default. When you await multiple tasks: [source,cpp] ---- -io_task fetch_a(); -io_task fetch_b(); -io_task fetch_c(); +task> fetch_a(); +task> fetch_b(); +task> fetch_c(); task<> sequential() { @@ -56,26 +56,26 @@ task<> concurrent() ---- All three fetches run in parallel. The `co_await` completes when the slowest -one finishes. Children must return `io_result<...>` (i.e., be `io_task`). +one finishes. Children must return `results<...>` (i.e., be `task>`). == Return Value -`when_all` returns `task>`, a single `ec` followed by each child's payload type. Each `Ri` is the child's payload flattened: `io_result` contributes `T`, `io_result<>` contributes `tuple<>`. +`when_all` returns `task>`, a single `ec` followed by each child's payload type. Each `Ri` is the child's payload flattened: `results` contributes `T`, `results<>` contributes `tuple<>`. [source,cpp] ---- // All non-void: single ec + flattened payloads auto [ec, x, y] = co_await when_all( - task_returning_int(), // io_task - task_returning_string() // io_task + task_returning_int(), // task> + task_returning_string() // task> ); // ec is std::error_code, x is int, y is std::string -// Mixed with void: io_task<> contributes tuple<> +// Mixed with void: task> contributes tuple<> auto [ec2, a, b, c] = co_await when_all( - task_returning_int(), // io_task — a is int - task_void(), // io_task<> — b is tuple<> - task_void() // io_task<> — c is tuple<> + task_returning_int(), // task> — a is int + task_void(), // task> — b is tuple<> + task_void() // task> — c is tuple<> ); // All void: just check ec @@ -91,7 +91,7 @@ Results appear in the same order as the input tasks. Values are only meaningful == Error Handling -There are two error paths. I/O errors are reported through the `ec` field of the returned `io_result`. Thrown exceptions are captured separately. +There are two error paths. I/O errors are reported through the `ec` field of the returned `results`. Thrown exceptions are captured separately. === I/O Errors (ec) @@ -99,7 +99,7 @@ When a child returns a non-zero `ec`: 1. Stop is requested for sibling tasks 2. All tasks are allowed to complete (or respond to stop) -3. The first `ec` is propagated in the outer `io_result` +3. The first `ec` is propagated in the outer `results` [source,cpp] ---- @@ -107,7 +107,7 @@ task<> handle_errors() { auto [ec, a, b] = co_await when_all( might_fail_io(), - another_io_task() + another_task() ); if (ec) std::cerr << "I/O error: " << ec.message() << "\n"; @@ -153,16 +153,16 @@ that support cancellation can respond by exiting early: [source,cpp] ---- -io_task<> cancellable_work() +task> cancellable_work() { auto token = co_await this_coro::stop_token; for (int i = 0; i < 1000; ++i) { if (token.stop_requested()) - co_return io_result<>{}; // Exit early + co_return results<>{}; // Exit early co_await do_chunk(i); } - co_return io_result<>{}; + co_return results<>{}; } task<> example() @@ -243,10 +243,10 @@ run_async(pool.get_executor())(parent()); [source,cpp] ---- -io_task fetch(http_client& client, std::string url) +task> fetch(http_client& client, std::string url) { auto [ec, body] = co_await client.get(url); - co_return io_result{ec, std::move(body)}; + co_return results{ec, std::move(body)}; } task<> fetch_all(http_client& client) @@ -290,10 +290,10 @@ Do NOT use `when_all` when: | Feature | Description | `when_all(tasks...)` -| Launch `io_task` children concurrently, wait for all +| Launch `task>` children concurrently, wait for all | Return type -| `task>` — single `ec` + flattened payloads +| `task>` — single `ec` + flattened payloads | Error handling | First `ec` or exception propagated, siblings get stop diff --git a/doc/unlisted/coroutines-when-any.adoc b/doc/unlisted/coroutines-when-any.adoc index 7d55ba3c4..44eb241f4 100644 --- a/doc/unlisted/coroutines-when-any.adoc +++ b/doc/unlisted/coroutines-when-any.adoc @@ -41,8 +41,8 @@ the first one *succeeds* (`!ec`): task<> race() { auto result = co_await when_any( - fetch_from_primary(), // io_task - fetch_from_backup() // io_task + fetch_from_primary(), // task> + fetch_from_backup() // task> ); // result is variant // result.index() == 0: all tasks failed @@ -67,8 +67,8 @@ When racing tasks with different return types: [source,cpp] ---- auto result = co_await when_any( - task_returning_int(), // io_task - task_returning_string() // io_task + task_returning_int(), // task> + task_returning_string() // task> ); if (result.index() == 0) @@ -83,13 +83,13 @@ The `result` variable is a `std::variant`. In === Void Tasks -`io_task<>` children contribute `std::tuple<>` to the variant: +`task>` children contribute `std::tuple<>` to the variant: [source,cpp] ---- auto result = co_await when_any( - task_returning_int(), // io_task - task_void() // io_task<> + task_returning_int(), // task> + task_void() // task> ); if (result.index() == 0) @@ -110,9 +110,9 @@ Use `.index()` to identify which task won: [source,cpp] ---- auto result = co_await when_any( - fetch_from_server_a(), // io_task - fetch_from_server_b(), // io_task - fetch_from_server_c() // io_task + fetch_from_server_a(), // task> + fetch_from_server_b(), // task> + fetch_from_server_c() // task> ); // result: variant @@ -136,7 +136,7 @@ For a dynamic number of tasks with the same type, use the range overload: [source,cpp] ---- -std::vector> requests; +std::vector>> requests; for (auto& server : servers) requests.push_back(fetch_from(server)); @@ -160,7 +160,7 @@ For void tasks in a range: [source,cpp] ---- -std::vector> tasks; +std::vector>> tasks; // ... populate tasks auto result = co_await when_any(std::move(tasks)); @@ -226,17 +226,17 @@ Tasks that support cancellation can exit early: [source,cpp] ---- -io_task fetch_with_cancel_support() +task> fetch_with_cancel_support() { auto token = co_await this_coro::stop_token; for (auto& chunk : data_source) { if (token.stop_requested()) - co_return io_result{std::error_code(), partial_response()}; + co_return results{std::error_code(), partial_response()}; co_await send_chunk(chunk); } - co_return io_result{std::error_code(), complete_response()}; + co_return results{std::error_code(), complete_response()}; } task<> example() @@ -313,7 +313,7 @@ Race requests to multiple servers for reliability: [source,cpp] ---- -io_task fetch_with_redundancy(Request req) +task> fetch_with_redundancy(Request req) { auto result = co_await when_any( fetch_from(primary_server, req), @@ -322,7 +322,7 @@ io_task fetch_with_redundancy(Request req) // result: variant if (result.index() == 0) - co_return io_result{std::get<0>(result), {}}; + co_return results{std::get<0>(result), {}}; std::cout << (result.index() == 1 ? "Primary" : "Backup") << " server responded\n"; @@ -333,7 +333,7 @@ io_task fetch_with_redundancy(Request req) if constexpr (!std::is_same_v, std::error_code>) resp = v; }, result); - co_return io_result{std::error_code(), std::move(resp)}; + co_return results{std::error_code(), std::move(resp)}; } ---- @@ -343,9 +343,9 @@ Wait for the first available connection from a pool: [source,cpp] ---- -io_task get_connection(std::vector& pools) +task> get_connection(std::vector& pools) { - std::vector> attempts; + std::vector>> attempts; for (auto& pool : pools) attempts.push_back(pool.acquire()); @@ -353,11 +353,11 @@ io_task get_connection(std::vector& pools) // result: variant> if (result.index() == 0) - co_return io_result{std::get<0>(result), {}}; + co_return results{std::get<0>(result), {}}; auto& [index, conn] = std::get<1>(result); std::cout << "Got connection from pool " << index << "\n"; - co_return io_result{std::error_code(), std::move(conn)}; + co_return results{std::error_code(), std::move(conn)}; } ---- @@ -374,7 +374,7 @@ This function creates an acquire task for each pool, then races them. Whichever | Returns on first success (`!ec`) | Return type -| `task>` +| `task>` | `task>` | Error handling @@ -393,10 +393,10 @@ This function creates an acquire task for each pool, then races them. Whichever | Feature | Description | `when_any(tasks...)` -| Race `io_task` children, return first success +| Race `task>` children, return first success | `when_any(range)` -| Race homogeneous `io_task` children from a range +| Race homogeneous `task>` children from a range | Return type (variadic) | `variant` — index 0 is failure, 1..N is winner diff --git a/doc/unlisted/library-cancellation.adoc b/doc/unlisted/library-cancellation.adoc index 267cf9c90..165364a00 100644 --- a/doc/unlisted/library-cancellation.adoc +++ b/doc/unlisted/library-cancellation.adoc @@ -90,7 +90,7 @@ Custom awaitables can support cancellation: struct stoppable_timer { std::chrono::milliseconds duration_; - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; bool cancelled_ = false; bool await_ready() const @@ -98,7 +98,7 @@ struct stoppable_timer return duration_.count() <= 0; } - void await_suspend(std::coroutine_handle<> h, io_env const* env) + void await_suspend(std::coroutine_handle<> h, await_context const* env) { env_ = env; // Store pointer, never copy diff --git a/doc/unlisted/library-executors.adoc b/doc/unlisted/library-executors.adoc index 6bad016e9..d677e733c 100644 --- a/doc/unlisted/library-executors.adoc +++ b/doc/unlisted/library-executors.adoc @@ -296,4 +296,4 @@ Use `work_guard` for RAII: == Next Steps * xref:frame-allocators.adoc[Frame Allocators] — Optimize coroutine allocation -* xref:../io-awaitables/executor.adoc[The Executor] — Affinity details +* xref:../context-awaitables/executor.adoc[The Executor] — Affinity details diff --git a/doc/unlisted/library-frame-allocators.adoc b/doc/unlisted/library-frame-allocators.adoc index df12b55fe..21285a047 100644 --- a/doc/unlisted/library-frame-allocators.adoc +++ b/doc/unlisted/library-frame-allocators.adoc @@ -244,4 +244,4 @@ If allocation is the bottleneck: == Next Steps * xref:executors.adoc[Executors and Strands] — Execution contexts -* xref:../io-awaitables/allocator.adoc[The Allocator] — Allocation window details +* xref:../context-awaitables/allocator.adoc[The Allocator] — Allocation window details diff --git a/doc/unlisted/library-io-result.adoc b/doc/unlisted/library-results.adoc similarity index 85% rename from doc/unlisted/library-io-result.adoc rename to doc/unlisted/library-results.adoc index bee5be075..54d29fb8a 100644 --- a/doc/unlisted/library-io-result.adoc +++ b/doc/unlisted/library-results.adoc @@ -7,9 +7,9 @@ // Official repository: https://github.com/cppalliance/capy // -= Error Handling with io_result += Error Handling with `results` -This section explains how to handle errors from I/O operations using `io_result` +This section explains how to handle errors from I/O operations using `results` and structured bindings. == The Problem: Exceptions vs Error Codes @@ -42,13 +42,13 @@ Explicit, but: * Easy to forget to check * Clutters the API -== io_result: The Best of Both +== `results`: The Best of Both -`io_result` combines an error code with optional result values: +`results` combines an error code with optional result values: [source,cpp] ---- -#include +#include // Returns error code + bytes transferred auto [ec, n] = co_await stream.read_some(buffer); @@ -87,11 +87,11 @@ if (ec) co_return ec; ---- -== io_result Variants +== `results` Variants -`io_result` supports 0-3 additional values: +`results` supports 0-3 additional values: -=== io_result<> (Error Only) +=== `results<>` (Error Only) Operations that succeed or fail without a result value: @@ -102,7 +102,7 @@ if (ec) co_return handle_connection_error(ec); ---- -=== io_result (Error + One Value) +=== `results` (Error + One Value) Operations returning a single value, like bytes transferred: @@ -114,7 +114,7 @@ if (ec) buffer.commit(n); // n bytes were read ---- -=== io_result (Error + Two Values) +=== `results` (Error + Two Values) Operations returning two values: @@ -123,7 +123,7 @@ Operations returning two values: auto [ec, bytes_read, endpoint] = co_await socket.receive_from(buffer); ---- -=== io_result (Error + Three Values) +=== `results` (Error + Three Values) Operations returning three values: @@ -134,7 +134,7 @@ auto [ec, v1, v2, v3] = co_await complex_operation(); == Using Structured Bindings -{cpp}17 structured bindings make `io_result` ergonomic: +{cpp}17 structured bindings make `results` ergonomic: [source,cpp] ---- @@ -149,9 +149,9 @@ The binding order matches the template parameters: [source,cpp] ---- -io_result → [ec, n] -io_result → [ec, n, ep] -io_result<> → [ec] +results → [ec, n] +results → [ec, n, ep] +results<> → [ec] ---- == Error Checking Idioms @@ -191,13 +191,13 @@ if (ec) throw system_error(ec); ---- -== Returning io_result from Functions +== Returning `results` from Functions -Custom awaitables can return `io_result`: +Custom awaitables can return `results`: [source,cpp] ---- -io_task read_all(stream& s, buffer& buf) +task> read_all(stream& s, buffer& buf) { std::size_t total = 0; while (buf.size() < buf.max_size()) @@ -226,7 +226,7 @@ io_task read_all(stream& s, buffer& buf) * Error handling is centralized (top-level catch) * Code would be cluttered with error checks -**Capy's recommendation:** Use `io_result` for I/O operations (errors are +**Capy's recommendation:** Use `results` for I/O operations (errors are common) and exceptions for programmer errors (bugs, invariant violations). == Converting Between Styles @@ -246,7 +246,7 @@ if (ec) [source,cpp] ---- template -auto throw_on_error(io_result result) +auto throw_on_error(results result) { auto& [ec, rest...] = result; if (ec) @@ -282,7 +282,7 @@ task read_all(stream& s, mutable_buffer buf) [source,cpp] ---- -io_task<> write_with_retry(stream& s, const_buffer data, int retries) +task> write_with_retry(stream& s, const_buffer data, int retries) { for (int i = 0; i < retries; ++i) { @@ -307,16 +307,16 @@ io_task<> write_with_retry(stream& s, const_buffer data, int retries) |=== | Type | Contents -| `io_result<>` +| `results<>` | Error code only -| `io_result` +| `results` | Error code + one value -| `io_result` +| `results` | Error code + two values -| `io_result` +| `results` | Error code + three values | Structured bindings diff --git a/doc/unlisted/library-streams.adoc b/doc/unlisted/library-streams.adoc index e3636c9f0..741629fef 100644 --- a/doc/unlisted/library-streams.adoc +++ b/doc/unlisted/library-streams.adoc @@ -100,8 +100,8 @@ A `ReadStream` provides partial read operations via `read_some()`: template concept ReadStream = requires(T& stream, MutableBufferSequence auto buffers) { - { stream.read_some(buffers) } -> IoAwaitable; - // Returns io_result + { stream.read_some(buffers) } -> ContextAwaitable; + // Returns results }; ---- @@ -153,8 +153,8 @@ A `WriteStream` provides partial write operations via `write_some()`: template concept WriteStream = requires(T& stream, ConstBufferSequence auto buffers) { - { stream.write_some(buffers) } -> IoAwaitable; - // Returns io_result + { stream.write_some(buffers) } -> ContextAwaitable; + // Returns results }; ---- @@ -211,8 +211,8 @@ A `ReadSource` provides complete read operations via `read()`: template concept ReadSource = requires(T& source, MutableBufferSequence auto buffers) { - { source.read(buffers) } -> IoAwaitable; - // Returns io_result + { source.read(buffers) } -> ContextAwaitable; + // Returns results }; ---- @@ -258,9 +258,9 @@ A `WriteSink` provides complete write operations with explicit EOF signaling: template concept WriteSink = requires(T& sink, ConstBufferSequence auto buffers, bool eof) { - { sink.write(buffers) } -> IoAwaitable; // Write all bytes - { sink.write(buffers, eof) } -> IoAwaitable; // Write + optional EOF - { sink.write_eof() } -> IoAwaitable; // Signal end of data + { sink.write(buffers) } -> ContextAwaitable; // Write all bytes + { sink.write(buffers, eof) } -> ContextAwaitable; // Write + optional EOF + { sink.write_eof() } -> ContextAwaitable; // Signal end of data }; ---- @@ -313,8 +313,8 @@ its internal storage: template concept BufferSource = requires(T& src, const_buffer* arr, std::size_t max_count) { - { src.pull(arr, max_count) } -> IoAwaitable; - // Returns io_result where size_t is buffer count + { src.pull(arr, max_count) } -> ContextAwaitable; + // Returns results where size_t is buffer count }; ---- @@ -377,9 +377,9 @@ concept BufferSink = requires(T& sink, mutable_buffer* arr, std::size_t max_coun std::size_t n, bool eof) { { sink.prepare(arr, max_count) } -> std::convertible_to; // Sync - { sink.commit(n) } -> IoAwaitable; // Commit n bytes - { sink.commit(n, eof) } -> IoAwaitable; // Commit + optional EOF - { sink.commit_eof() } -> IoAwaitable; // Signal end of data + { sink.commit(n) } -> ContextAwaitable; // Commit n bytes + { sink.commit(n, eof) } -> ContextAwaitable; // Commit + optional EOF + { sink.commit_eof() } -> ContextAwaitable; // Signal end of data }; ---- diff --git a/doc/unlisted/library-task.adoc b/doc/unlisted/library-task.adoc index c8f14226e..cea20c4c8 100644 --- a/doc/unlisted/library-task.adoc +++ b/doc/unlisted/library-task.adoc @@ -171,7 +171,7 @@ A coroutine has unique state that cannot be duplicated. == Affinity and Context -Tasks implement `IoRunnable`, receiving execution environment when +Tasks implement `ContextRunnable`, receiving execution environment when awaited: [source,cpp] @@ -192,7 +192,7 @@ task parent() // Bound to pool's executor run_async(pool.get_executor())(parent()); ---- -See xref:../io-awaitables/concepts.adoc[Concept Hierarchy] for details. +See xref:../context-awaitables/concepts.adoc[Concept Hierarchy] for details. == Advanced Operations @@ -260,6 +260,6 @@ After `release()`, the task is empty and must not be awaited. == Next Steps -* xref:io-result.adoc[Error Handling with io_result] — Handle I/O errors +* xref:results.adoc[Error Handling with `results`] — Handle I/O errors * xref:when-all.adoc[Concurrent Composition] — Run tasks in parallel -* xref:../io-awaitables/launching.adoc[Launching Coroutines] — Start tasks with `run_async` +* xref:../context-awaitables/launching.adoc[Launching Coroutines] — Start tasks with `run_async` diff --git a/example/asio/README.md b/example/asio/README.md index dc2e58ed5..923c5ec01 100644 --- a/example/asio/README.md +++ b/example/asio/README.md @@ -90,7 +90,7 @@ int main() ## use_capy_example.cpp -This example demonstrates the `use_capy` completion token, which allows **any Asio async operation** to be awaited directly from a Capy coroutine. The returned awaitable satisfies Capy's `IoAwaitable` concept with the extended `await_suspend` signature. +This example demonstrates the `use_capy` completion token, which allows **any Asio async operation** to be awaited directly from a Capy coroutine. The returned awaitable satisfies Capy's `ContextAwaitable` concept with the extended `await_suspend` signature. ```cpp capy::task<> @@ -119,9 +119,9 @@ reader(net::ip::tcp::socket& socket, std::size_t total) The `use_capy` token: - Works with any Asio async operation -- Returns an `IoAwaitable` that integrates with Capy's executor and cancellation +- Returns a `ContextAwaitable` that integrates with Capy's executor and cancellation - Bridges `std::stop_token` to Asio's cancellation system -- Returns results as `capy::io_result<...>` for structured bindings +- Returns results as `capy::results<...>` for structured bindings - Uses `asio::deferred_async_operation` internally (no `std::function` overhead) --- diff --git a/example/asio/any_stream.cpp b/example/asio/any_stream.cpp index 7165cdcc9..25e1627f9 100644 --- a/example/asio/any_stream.cpp +++ b/example/asio/any_stream.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include #include @@ -24,7 +24,7 @@ constexpr std::size_t total_bytes = 1024; // tag::writer_reader[] -capy::io_task<> +capy::task> writer( capy::any_stream& stream, std::size_t total) @@ -40,16 +40,16 @@ writer( if(ec) { std::printf("writer error: %s\n", ec.message().c_str()); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } written += n; std::printf("writer: wrote %zu bytes (total %zu)\n", n, written); } std::printf("writer: done, wrote %zu bytes\n", written); - co_return capy::io_result<>{}; + co_return capy::results<>{}; } -capy::io_task<> +capy::task> reader( capy::any_stream& stream, std::size_t total) @@ -63,13 +63,13 @@ reader( if(ec) { std::printf("reader error: %s\n", ec.message().c_str()); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } read_total += n; std::printf("reader: read %zu bytes (total %zu)\n", n, read_total); } std::printf("reader: done, read %zu bytes\n", read_total); - co_return capy::io_result<>{}; + co_return capy::results<>{}; } // end::writer_reader[] diff --git a/example/asio/api/capy_streams.cpp b/example/asio/api/capy_streams.cpp index dd630208a..94499a5a4 100644 --- a/example/asio/api/capy_streams.cpp +++ b/example/asio/api/capy_streams.cpp @@ -21,8 +21,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -81,7 +81,7 @@ class asio_socket { asio_socket* self_; MB buffers_; - capy::io_result result_; + capy::results result_; std::shared_ptr cancel_; capy::continuation cont_; @@ -102,7 +102,7 @@ class asio_socket std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { cont_.h = h; cont_.owner = env->owner; @@ -132,7 +132,7 @@ class asio_socket return std::noop_coroutine(); } - capy::io_result + capy::results await_resume() { cancel_.reset(); @@ -154,7 +154,7 @@ class asio_socket { asio_socket* self_; CB buffers_; - capy::io_result result_; + capy::results result_; std::shared_ptr cancel_; capy::continuation cont_; @@ -175,7 +175,7 @@ class asio_socket std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { cont_.h = h; cont_.owner = env->owner; @@ -205,7 +205,7 @@ class asio_socket return std::noop_coroutine(); } - capy::io_result + capy::results await_resume() { cancel_.reset(); diff --git a/example/asio/api/use_capy.hpp b/example/asio/api/use_capy.hpp index cda84e9ab..c1a8d824a 100644 --- a/example/asio/api/use_capy.hpp +++ b/example/asio/api/use_capy.hpp @@ -19,9 +19,9 @@ #include #include +#include #include -#include -#include +#include #include #include @@ -65,7 +65,7 @@ struct cancel_bridge //---------------------------------------------------------- // -// result_type_for - Maps completion signature to io_result +// result_type_for - Maps completion signature to `results` // //---------------------------------------------------------- @@ -75,25 +75,25 @@ struct result_type_for; template<> struct result_type_for { - using type = capy::io_result<>; + using type = capy::results<>; }; template struct result_type_for { - using type = capy::io_result>; + using type = capy::results>; }; template struct result_type_for { - using type = capy::io_result, std::decay_t>; + using type = capy::results, std::decay_t>; }; template struct result_type_for { - using type = capy::io_result, std::decay_t, std::decay_t>; + using type = capy::results, std::decay_t, std::decay_t>; }; template @@ -101,7 +101,7 @@ using result_type_for_t = typename result_type_for::type; //---------------------------------------------------------- // -// capy_awaitable - IoAwaitable wrapping a deferred operation +// capy_awaitable - ContextAwaitable wrapping a deferred operation // //---------------------------------------------------------- @@ -129,7 +129,7 @@ class capy_awaitable // tag::await_suspend[] std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { cancel_ = std::make_shared(env->stop_token); @@ -211,10 +211,10 @@ class capy_awaitable // //---------------------------------------------------------- -/** Completion token that returns an IoAwaitable. +/** Completion token that returns a ContextAwaitable. This completion token can be passed to any Asio async operation - to receive an awaitable that satisfies capy's IoAwaitable concept. + to receive an awaitable that satisfies capy's ContextAwaitable concept. The awaitable uses the extended await_suspend signature that accepts an executor and stop token. diff --git a/example/asio/use_capy_example.cpp b/example/asio/use_capy_example.cpp index 17aee661b..974300914 100644 --- a/example/asio/use_capy_example.cpp +++ b/example/asio/use_capy_example.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include #include @@ -26,9 +26,9 @@ constexpr std::size_t total_bytes = 1024; // Writer coroutine using use_capy with raw Asio socket. -// Returns io_task<> so it can be composed with when_all. +// Returns task> so it can be composed with when_all. // tag::writer[] -capy::io_task<> +capy::task> writer( net::ip::tcp::socket& socket, std::size_t total) @@ -48,19 +48,19 @@ writer( if (ec) { std::printf("writer error: %s\n", ec.message().c_str()); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } written += n; std::printf("writer: wrote %zu bytes (total %zu)\n", n, written); } std::printf("writer: done, wrote %zu bytes\n", written); - co_return capy::io_result<>{}; + co_return capy::results<>{}; } // end::writer[] // Reader coroutine using use_capy with raw Asio socket. -// Returns io_task<> so it can be composed with when_all. -capy::io_task<> +// Returns task> so it can be composed with when_all. +capy::task> reader( net::ip::tcp::socket& socket, std::size_t total) @@ -77,13 +77,13 @@ reader( if (ec) { std::printf("reader error: %s\n", ec.message().c_str()); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } read_total += n; std::printf("reader: read %zu bytes (total %zu)\n", n, read_total); } std::printf("reader: done, read %zu bytes\n", read_total); - co_return capy::io_result<>{}; + co_return capy::results<>{}; } capy::task<> diff --git a/example/async-mutex/async_mutex.cpp b/example/async-mutex/async_mutex.cpp index 654263930..ae260ab7d 100644 --- a/example/async-mutex/async_mutex.cpp +++ b/example/async-mutex/async_mutex.cpp @@ -47,7 +47,7 @@ int main() int acquisition_order = 0; std::vector order_log; - auto worker = [&](int id) -> capy::io_task<> { + auto worker = [&](int id) -> capy::task> { std::cout << "Worker " << id << " waiting for lock\n"; // tag::scoped_lock[] auto [ec, guard] = co_await mtx.scoped_lock(); @@ -55,7 +55,7 @@ int main() { std::cout << "Worker " << id << " canceled: " << ec.message() << "\n"; - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } // end::scoped_lock[] @@ -65,7 +65,7 @@ int main() << " acquired lock (sequence " << seq << ")\n"; std::cout << "Worker " << id << " releasing lock\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; }; auto run_all = [&]() -> capy::task<> { diff --git a/example/awaitable-sender/awaitable_sender.cpp b/example/awaitable-sender/awaitable_sender.cpp index 58c18f907..13aa93f55 100644 --- a/example/awaitable-sender/awaitable_sender.cpp +++ b/example/awaitable-sender/awaitable_sender.cpp @@ -33,10 +33,10 @@ struct demo_receiver { using receiver_concept = ex::receiver_t; - capy::io_sender_env env_; + capy::context_sender_env env_; std::latch* done_; - auto get_env() const noexcept -> capy::io_sender_env + auto get_env() const noexcept -> capy::context_sender_env { return env_; } @@ -83,7 +83,7 @@ int main() // wait() requires resumption on a single thread. capy::thread_pool pool(1); - // Named so io_sender_env's executor_ref (which stores a + // Named so context_sender_env's executor_ref (which stores a // pointer to whatever it is given) points at a stable object // instead of a temporary that dies at the end of the // full-expression that constructs each demo_receiver. @@ -103,7 +103,7 @@ int main() std::latch done(1); - // Build a sender from a Capy IoAwaitable + // Build a sender from a Capy ContextAwaitable auto sndr = capy::as_sender(waker_1.wait()); // Connect with a receiver whose environment carries diff --git a/example/awaitable-sender/awaitable_sender.hpp b/example/awaitable-sender/awaitable_sender.hpp index 29b0a49c5..85e8b6ab1 100644 --- a/example/awaitable-sender/awaitable_sender.hpp +++ b/example/awaitable-sender/awaitable_sender.hpp @@ -14,7 +14,7 @@ #include "awaitable_sender_base.hpp" #include "awaitable_sender_detail.hpp" -#include +#include #include @@ -24,23 +24,23 @@ namespace boost::capy { // ------------------------------------------------------- -// Sender that wraps an IoAwaitable +// Sender that wraps a ContextAwaitable // ------------------------------------------------------- -/** A sender that wraps an IoAwaitable. +/** A sender that wraps a ContextAwaitable. - Adapts an IoAwaitable to the `std::execution` sender + Adapts a ContextAwaitable to the `std::execution` sender concept, enabling composition with other sender operations and adapters. */ -template +template struct awaitable_sender { using sender_concept = ex::sender_t; - IoAw aw_; + CtxAw aw_; - /** Return the completion signatures deduced from `IoAw`. + /** Return the completion signatures deduced from `CtxAw`. C++26 static-template form (see @ref awaitable_sender_base for the mechanism notes). @@ -48,29 +48,29 @@ struct awaitable_sender template static consteval auto get_completion_signatures() noexcept { - return decltype(detail::make_sigs()){}; + return decltype(detail::make_sigs()){}; } /// Connect this sender with a receiver to form an operation state. template auto connect(Receiver rcvr) && - -> detail::awaitable_op_state + -> detail::awaitable_op_state { - return detail::awaitable_op_state( + return detail::awaitable_op_state( std::move(aw_), std::move(rcvr)); } /// Connect a copy of the sender to a receiver. template auto connect(Receiver rcvr) const& - -> detail::awaitable_op_state + -> detail::awaitable_op_state { - return detail::awaitable_op_state( + return detail::awaitable_op_state( aw_, std::move(rcvr)); } }; -/** Create a `std::execution` sender from an IoAwaitable. +/** Create a `std::execution` sender from a ContextAwaitable. The bridge routes the awaitable's result through sender channels based on its type: @@ -78,14 +78,14 @@ struct awaitable_sender - `void` - calls `set_value()`. - `error_code`, or any single-element tuple-like whose sole element is `error_code` (such as an empty - `io_result`) - calls `set_value()` when the code is + `results`) - calls `set_value()` when the code is zero, `set_error(ec)` otherwise. - Any other single value `T` - calls `set_value(T)`. Compound results are rejected at compile time, and the constraint is structural, not nominal: any tuple-like whose element 0 is `error_code` with additional elements is - refused, whether it is `io_result`, + refused, whether it is `results`, `std::tuple`, or a user-defined type of the same shape. Completion channels are exclusive, so a partial success (an `error_code` arriving alongside bytes @@ -108,14 +108,14 @@ struct awaitable_sender auto sndr = as_sender(waker.wait()); @endcode - @param aw The IoAwaitable to wrap. + @param aw The ContextAwaitable to wrap. @return A sender whose completion channels reflect the awaitable's result type. */ -template -auto as_sender(IoAw&& aw) +template +auto as_sender(CtxAw&& aw) { - using R = awaitable_result_t>; + using R = awaitable_result_t>; static_assert( !detail::is_compound_ec_result_v, "as_sender does not accept awaitables whose result " @@ -124,22 +124,22 @@ auto as_sender(IoAw&& aw) "(error_code plus payload) would be silently dropped. " "Wrap the operation in a task that inspects " "the full result and returns the error code."); - return awaitable_sender>{ - std::forward(aw)}; + return awaitable_sender>{ + std::forward(aw)}; } /** Return the awaitable as a sender. Boundary normalizer for generic code handed an arbitrary - @ref IoAwaitable: an op that already models + @ref ContextAwaitable: an op that already models @ref AwaitableSender passes through unchanged, anything else is lifted with @ref as_sender. Either way the result is a sender by value. - @param a The IoAwaitable to normalize. + @param a The ContextAwaitable to normalize. @return `a` itself, or `as_sender(a)`. */ -template +template auto ensure_sender(A&& a) { if constexpr (AwaitableSender>) diff --git a/example/awaitable-sender/awaitable_sender_base.hpp b/example/awaitable-sender/awaitable_sender_base.hpp index 4a8fe34ce..7f7abda33 100644 --- a/example/awaitable-sender/awaitable_sender_base.hpp +++ b/example/awaitable-sender/awaitable_sender_base.hpp @@ -18,7 +18,7 @@ namespace boost::capy { /** Concept for awaitables that are also senders. - Refines @ref IoAwaitable with the `std::execution` sender + Refines @ref ContextAwaitable with the `std::execution` sender requirements: the same op can be driven by `co_await` in a capy coroutine and by `connect`/`start` in a sender pipeline. Both halves are structural — deriving @ref awaitable_sender_base is @@ -40,10 +40,10 @@ namespace boost::capy { */ template concept AwaitableSender = - IoAwaitable && + ContextAwaitable && ex::sender; -/** CRTP mixin that makes an IoAwaitable a sender. +/** CRTP mixin that makes a ContextAwaitable a sender. Deriving from this base adds the C++26 sender interface to an I/O awaitable, so the same op type works under `co_await` @@ -77,7 +77,7 @@ concept AwaitableSender = @code struct read_op : awaitable_sender_base { - // usual IoAwaitable members; the completion signatures + // usual ContextAwaitable members; the completion signatures // follow await_resume()'s result type }; @endcode diff --git a/example/awaitable-sender/awaitable_sender_detail.hpp b/example/awaitable-sender/awaitable_sender_detail.hpp index e4a816210..2518cf4eb 100644 --- a/example/awaitable-sender/awaitable_sender_detail.hpp +++ b/example/awaitable-sender/awaitable_sender_detail.hpp @@ -10,13 +10,13 @@ #ifndef BOOST_CAPY_EXAMPLE_AWAITABLE_SENDER_DETAIL_HPP #define BOOST_CAPY_EXAMPLE_AWAITABLE_SENDER_DETAIL_HPP -#include +#include #include #include +#include #include #include -#include -#include +#include #include @@ -39,7 +39,7 @@ namespace ex = stdexec; // CPO: query a receiver environment for a Capy executor // ------------------------------------------------------- -struct get_io_executor_t +struct get_context_executor_t : ex::forwarding_query_t { // Derive from forwarding_query_t so env-wrapping adaptors @@ -48,28 +48,28 @@ struct get_io_executor_t // forwarding_query, filtering out the rest by default. template auto operator()(Env const& env) const noexcept - -> decltype(env.query(std::declval())) + -> decltype(env.query(std::declval())) { return env.query(*this); } }; -inline constexpr get_io_executor_t get_io_executor{}; +inline constexpr get_context_executor_t get_context_executor{}; // ------------------------------------------------------- // Environment that carries a Capy executor + stop token // ------------------------------------------------------- -struct io_sender_env +struct context_sender_env { - executor_ref io_executor; + executor_ref context_executor; std::stop_token stop_token; auto query( - get_io_executor_t const&) const noexcept + get_context_executor_t const&) const noexcept -> executor_ref { - return io_executor; + return context_executor; } auto query( @@ -162,7 +162,7 @@ auto make_sigs() static_assert( !is_compound_ec_result_v, - "IoAwaitables whose result destructures into " + "ContextAwaitables whose result destructures into " "(error_code, ...) cannot be senders: completion " "channels are exclusive, so a partial success " "(error_code plus payload) would be silently dropped. " @@ -230,7 +230,7 @@ struct is_polymorphic_allocator< // Bridges a std::execution scheduler to capy's Executor concept so // ops can run under receivers that provide only get_scheduler // (e.g. sync_wait's run_loop). Each post allocates a small holder -// for the schedule operation; receivers supplying get_io_executor +// for the schedule operation; receivers supplying get_context_executor // never pay this. template class scheduler_executor @@ -345,7 +345,7 @@ struct awaitable_op_state using stop_token_type = typename env_stop_token::type; - // io_env only carries std::stop_token, so any other + // await_context only carries std::stop_token, so any other // stoppable token needs an owned std::stop_source relayed // through a stop_callback; unstoppable tokens need neither. // The stoppable_token conjunct keeps stop_callback_for_t @@ -380,8 +380,8 @@ struct awaitable_op_state return std::type_identity{}; } - static constexpr bool has_io_executor = - requires(env_type const& e) { get_io_executor(e); }; + static constexpr bool has_context_executor = + requires(env_type const& e) { get_context_executor(e); }; static constexpr bool has_scheduler = requires(env_type const& e) { ex::get_scheduler(e); }; @@ -390,7 +390,7 @@ struct awaitable_op_state struct no_scheduler_bridge {}; static auto sched_ex_storage() { - if constexpr (!has_io_executor && has_scheduler) + if constexpr (!has_context_executor && has_scheduler) return std::type_identity()))>>>{}; @@ -401,7 +401,7 @@ struct awaitable_op_state // Reset to destroy the segment when it is abandoned. std::optional aw_; Receiver rcvr_; - io_env env_; + await_context env_; frame_cb cb_; // The segment's owner, with cb_ as its coroutine. chain_owner owner_; @@ -550,16 +550,16 @@ struct awaitable_op_state { auto renv = ex::get_env(rcvr_); - static_assert(has_io_executor || has_scheduler, + static_assert(has_context_executor || has_scheduler, "the receiver's environment must provide " - "capy::get_io_executor or a scheduler via " + "capy::get_context_executor or a scheduler via " "std::execution::get_scheduler"); executor_ref io_ex = [&]() -> executor_ref { - if constexpr (has_io_executor) + if constexpr (has_context_executor) { - return get_io_executor(renv); + return get_context_executor(renv); } else { @@ -582,7 +582,7 @@ struct awaitable_op_state on_stop{stop_src_}); st = stop_src_.get_token(); } - // Map the environment's allocator into io_env when it + // Map the environment's allocator into await_context when it // speaks pmr; other allocator types have no // memory_resource to lend. std::pmr::memory_resource* fa = nullptr; @@ -594,7 +594,7 @@ struct awaitable_op_state fa = alloc.resource(); } - env_ = io_env{io_ex, st, fa}; + env_ = await_context{io_ex, st, fa}; env_.owner = &owner_; if(aw_->await_ready()) diff --git a/example/awaitable-sender/read_op.hpp b/example/awaitable-sender/read_op.hpp index cc04e4370..94f4fd47e 100644 --- a/example/awaitable-sender/read_op.hpp +++ b/example/awaitable-sender/read_op.hpp @@ -14,8 +14,8 @@ #include #include -#include -#include +#include +#include #include #include @@ -33,7 +33,7 @@ namespace boost::capy { `async_waker` rather than hand-rolling stop-callback arming: the waker's arbiter already resolves the races between arming, stop requests, and completion. Completes with a bare - `io_result<>`: a result carrying payload alongside the + `results<>`: a result carrying payload alongside the `error_code` cannot be a sender (see @ref as_sender). */ class read_op : public awaitable_sender_base @@ -45,16 +45,16 @@ class read_op : public awaitable_sender_base std::error_code ec_{}; bool immediate_ = false; - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; continuation cont_{}; // Stop-wait mode: a coroutine owning a waker nobody wakes, so // it completes only when the environment's stop token fires // (with error::canceled). The waker lives in the coroutine // frame; the task is movable, keeping read_op movable. - std::optional>> stop_task_; + std::optional>> stop_task_; - static task> stop_wait() + static task> stop_wait() { async_waker waker; co_return co_await waker.wait(); @@ -96,7 +96,7 @@ class read_op : public awaitable_sender_base std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { if(stop_task_) return stop_task_->await_suspend(h, env); @@ -106,7 +106,7 @@ class read_op : public awaitable_sender_base return std::noop_coroutine(); } - io_result<> await_resume() noexcept + results<> await_resume() noexcept { // The op reports its own disposition in-band: a stop-wait // ends with the waker's error::canceled, which the sender @@ -137,7 +137,7 @@ class counted_read_op std::size_t n_ = 0; std::size_t* n_out_ = nullptr; - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; continuation cont_{}; public: @@ -160,7 +160,7 @@ class counted_read_op std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { env_ = env; cont_ = continuation{h, nullptr, env->owner}; @@ -168,7 +168,7 @@ class counted_read_op return std::noop_coroutine(); } - io_result<> await_resume() noexcept + results<> await_resume() noexcept { // Written before either protocol observes completion, so // the count is valid on every channel, including diff --git a/example/awaitable-sender/tests.cpp b/example/awaitable-sender/tests.cpp index a7ddf5cb6..18795c3ee 100644 --- a/example/awaitable-sender/tests.cpp +++ b/example/awaitable-sender/tests.cpp @@ -62,7 +62,7 @@ struct test_env capy::executor_ref ex_; Token tok_; - auto query(capy::get_io_executor_t const&) const noexcept + auto query(capy::get_context_executor_t const&) const noexcept -> capy::executor_ref { return ex_; @@ -178,20 +178,20 @@ bool await_latch( } \ } while(0) -// Mock IoAwaitable with a scripted error_code result, +// Mock ContextAwaitable with a scripted error_code result, // completing through the environment's executor. struct ec_op { std::error_code ec_{}; - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { env_ = env; cont_ = capy::continuation{h}; @@ -202,20 +202,20 @@ struct ec_op std::error_code await_resume() noexcept { return ec_; } }; -// Mock IoAwaitable with a scripted compound (ec, n) result. +// Mock ContextAwaitable with a scripted compound (ec, n) result. struct compound_op { std::error_code ec_{}; std::size_t n_ = 0; - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { env_ = env; cont_ = capy::continuation{h}; @@ -223,7 +223,7 @@ struct compound_op return std::noop_coroutine(); } - capy::io_result await_resume() noexcept + capy::results await_resume() noexcept { return {ec_, n_}; } @@ -243,19 +243,19 @@ capy::task read_some( static_assert(capy::AwaitableSender); -// Mock IoAwaitable that completes with no payload at all -// (the `void` row of make_sigs, distinct from io_result<>'s +// Mock ContextAwaitable that completes with no payload at all +// (the `void` row of make_sigs, distinct from `results<>`'s // 1-tuple-ec row exercised by async_waker). struct void_op { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { env_ = env; cont_ = capy::continuation{h}; @@ -266,18 +266,18 @@ struct void_op void await_resume() noexcept {} }; -// Mock IoAwaitable returning a plain value type, unrelated to +// Mock ContextAwaitable returning a plain value type, unrelated to // error_code (the "other T" row of make_sigs). struct value_op { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { env_ = env; cont_ = capy::continuation{h}; @@ -288,18 +288,18 @@ struct value_op int await_resume() noexcept { return 17; } }; -// Mock IoAwaitable whose await_resume() can throw, exercising +// Mock ContextAwaitable whose await_resume() can throw, exercising // awaitable_op_state's conditional try/catch -> set_error(exception_ptr). struct throwing_op { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { env_ = env; cont_ = capy::continuation{h}; @@ -391,7 +391,7 @@ void test_value_survives_racing_stop() CHECK(out.ch == channel::value); } -void test_empty_io_result_success() +void test_empty_results_success() { capy::thread_pool pool(1); auto pool_ex = pool.get_executor(); @@ -599,7 +599,7 @@ void test_read_op_then_pipeline() } // sync_wait's environment provides get_scheduler but neither -// get_io_executor nor a stop token; these exercise the scheduler +// get_context_executor nor a stop token; these exercise the scheduler // bridge end-to-end on the run_loop. void test_sync_wait_value() { @@ -636,14 +636,14 @@ struct alloc_probe_op { std::pmr::memory_resource** out_; - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_{}; bool await_ready() const noexcept { return false; } auto await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) + capy::await_context const* env) { *out_ = env->frame_allocator; env_ = env; @@ -660,7 +660,7 @@ struct pmr_env capy::executor_ref ex_; std::pmr::memory_resource* mr_; - auto query(capy::get_io_executor_t const&) const noexcept + auto query(capy::get_context_executor_t const&) const noexcept -> capy::executor_ref { return ex_; @@ -694,7 +694,7 @@ struct pmr_receiver void set_stopped() && noexcept { done_->count_down(); } }; -void test_env_allocator_reaches_io_env() +void test_env_allocator_reaches_await_context() { capy::thread_pool pool(1); auto pool_ex = pool.get_executor(); @@ -799,13 +799,13 @@ void test_coawait_stopped_midflight() // Channel splitting is structural, not nominal (P4093): any // tuple-like leading with error_code splits when it carries no -// payload and is rejected when it does, io_result or not. +// payload and is rejected when it does, `results` or not. static_assert(capy::detail::is_ec_outcome_v); -static_assert(capy::detail::is_ec_outcome_v>); +static_assert(capy::detail::is_ec_outcome_v>); static_assert(capy::detail::is_ec_outcome_v< std::tuple>); static_assert(capy::detail::is_compound_ec_result_v< - capy::io_result>); + capy::results>); static_assert(capy::detail::is_compound_ec_result_v< std::tuple>); static_assert(capy::detail::is_compound_ec_result_v< @@ -820,10 +820,10 @@ static_assert(!capy::detail::is_compound_ec_result_v< // both protocols; compound_op is awaitable-only; the as_sender // wrapper is sender-only (no await_suspend). static_assert(capy::AwaitableSender); -static_assert(capy::IoAwaitable && +static_assert(capy::ContextAwaitable && !capy::AwaitableSender); static_assert( - !capy::IoAwaitable> && + !capy::ContextAwaitable> && !capy::AwaitableSender>); // ensure_sender: identity for dual-protocol ops, lifting for @@ -860,7 +860,7 @@ struct held_op std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) noexcept + capy::await_context const* env) noexcept { slot_->h = h; slot_->owner = env->owner; @@ -907,7 +907,7 @@ struct unowned_held_op std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const*) noexcept + capy::await_context const*) noexcept { slot_->h = h; parked_->count_down(); @@ -1002,7 +1002,7 @@ void test_posted_op_abandon_unwinds_launch(Op op) int main() { - test_empty_io_result_success(); + test_empty_results_success(); test_void_op_success(); test_value_op_success(); test_throwing_op_error(); @@ -1027,7 +1027,7 @@ int main() test_sync_wait_value(); test_sync_wait_pipeline(); test_sync_wait_error_throws(); - test_env_allocator_reaches_io_env(); + test_env_allocator_reaches_await_context(); test_coawait_matches_sender_success(); test_coawait_matches_sender_error(); test_coawait_stopped_before_start(); diff --git a/example/cuda/batched-write/batched_write.cu b/example/cuda/batched-write/batched_write.cu index 7e4f4d9a2..5a910657b 100644 --- a/example/cuda/batched-write/batched_write.cu +++ b/example/cuda/batched-write/batched_write.cu @@ -30,7 +30,7 @@ namespace capy = boost::capy; namespace ex = capy::example; -capy::io_task +capy::task> write_batch(capy::any_write_stream& dest, std::array const& bufs) { @@ -61,10 +61,10 @@ int main() capy::make_buffer(parts[2].data(), parts[2].size())}; capy::thread_pool pool(2); - capy::io_result r; + capy::results r; std::latch done{1}; capy::run_async(pool.get_executor(), - [&](capy::io_result ir) { r = ir; done.count_down(); })( + [&](capy::results ir) { r = ir; done.count_down(); })( write_batch(dest, bufs)); done.wait(); auto const& [ec, n] = r; diff --git a/example/cuda/datamovement/README.md b/example/cuda/datamovement/README.md index 2e7d919c2..b49d2baec 100644 --- a/example/cuda/datamovement/README.md +++ b/example/cuda/datamovement/README.md @@ -9,10 +9,10 @@ at runtime; `example/cuda/batched-write` runs the batched `write_some`. What is validated: -- `cuda_stream_awaiter`: the io_env-less baseline. Asserted to be a - standard awaitable but **not** an `IoAwaitable`. +- `cuda_stream_awaiter`: the await_context-less baseline. Asserted to be a + standard awaitable but **not** a `ContextAwaitable`. - `cuda_stream`: `memcpy_h2d` / `memcpy_d2h` / `synchronize` return - `IoAwaitable`s. Because `cudaLaunchHostFunc` passes no status to its + `ContextAwaitable`s. Because `cudaLaunchHostFunc` passes no status to its host function, `await_resume` calls `cudaStreamQuery` after resumption so a stream fault surfaces as an error instead of success (see the `--fault` probe in `../notification-strategies`). diff --git a/example/cuda/datamovement/cuda_datamovement.cu b/example/cuda/datamovement/cuda_datamovement.cu index 569242798..7dfe9c999 100644 --- a/example/cuda/datamovement/cuda_datamovement.cu +++ b/example/cuda/datamovement/cuda_datamovement.cu @@ -10,7 +10,7 @@ #include "cuda_datamovement.hpp" #include -#include +#include #include #include @@ -22,22 +22,22 @@ namespace capy = boost::capy; namespace ex = capy::example; -// Intentionally io_env-less: a standard awaitable, not an IoAwaitable. -static_assert(! capy::IoAwaitable); +// Intentionally await_context-less: a standard awaitable, not a ContextAwaitable. +static_assert(! capy::ContextAwaitable); // The data-movement awaitables depend on this helper, which the paper // references but never defines. static_assert(std::is_same_v< decltype(ex::make_cuda_error(cudaSuccess)), std::error_code>); -// The memcpy member functions return IoAwaitables. -static_assert(capy::IoAwaitable< +// The memcpy member functions return ContextAwaitables. +static_assert(capy::ContextAwaitable< decltype(std::declval().memcpy_h2d( nullptr, nullptr, std::size_t{0}))>); -static_assert(capy::IoAwaitable< +static_assert(capy::ContextAwaitable< decltype(std::declval().memcpy_d2h( nullptr, nullptr, std::size_t{0}))>); -static_assert(capy::IoAwaitable< +static_assert(capy::ContextAwaitable< decltype(std::declval().synchronize())>); // GPU device memory satisfies WriteStream and type-erases with zero @@ -75,7 +75,7 @@ link_check() #include // NCCL interop: a collective enqueues onto the CUDA stream, then -// synchronize() awaits its completion through the same IoAwaitable path. +// synchronize() awaits its completion through the same ContextAwaitable path. capy::task<> all_reduce(ex::cuda_stream& cs, ncclComm_t comm, float const* sendbuf, float* recvbuf, std::size_t count) diff --git a/example/cuda/datamovement/cuda_datamovement.hpp b/example/cuda/datamovement/cuda_datamovement.hpp index 308754b6b..6af4f21d3 100644 --- a/example/cuda/datamovement/cuda_datamovement.hpp +++ b/example/cuda/datamovement/cuda_datamovement.hpp @@ -11,9 +11,9 @@ #define BOOST_CAPY_EXAMPLE_CUDA_DATAMOVEMENT_HPP #include -#include +#include #include -#include +#include #include #include @@ -98,7 +98,7 @@ struct cuda_stream_awaiter } }; -/// A CUDA stream whose data-movement operations are IoAwaitables. +/// A CUDA stream whose data-movement operations are ContextAwaitables. /// /// `memcpy_h2d`/`memcpy_d2h` issue a `cudaMemcpyAsync` and resume the /// awaiting coroutine on `env->executor` when the stream's @@ -143,7 +143,7 @@ class cuda_stream } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { auto err = cudaMemcpyAsync( dst, src, count, kind, self->stream_); @@ -185,7 +185,7 @@ class cuda_stream } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { self->cont_.h = h; self->cont_.owner = env->owner; @@ -278,7 +278,7 @@ class cuda_stream /// single host function follows the last, so the stream keeps its queue /// depth and the coroutine suspends once per `write_some`. Because /// `cudaMemcpyAsync` transfers each buffer in one operation, `write_some` -/// never performs a partial write. Errors are delivered via `io_result` +/// never performs a partial write. Errors are delivered via `results` /// rather than exceptions. Does not own `stream_`; the caller is /// responsible for the stream's lifetime. class cuda_device_stream @@ -326,7 +326,7 @@ class cuda_device_stream } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { // Enqueue the whole sequence before the host function so // the stream runs the batch back to back. @@ -359,7 +359,7 @@ class cuda_device_stream return std::noop_coroutine(); } - io_result + results await_resume() { if(! self->error_) diff --git a/example/cuda/notification-strategies/README.md b/example/cuda/notification-strategies/README.md index 5b80fc85b..1c131a1f9 100644 --- a/example/cuda/notification-strategies/README.md +++ b/example/cuda/notification-strategies/README.md @@ -4,8 +4,8 @@ One GPU completion, three notification mechanisms, one protocol. The same pipeline (fill a device buffer, copy it back to host, await the CUDA stream) is awaited three structurally different ways. All three are -`boost::capy::IoAwaitable`s, and all three produce the identical result -at runtime. This demonstrates that the IoAwaitable protocol is +`boost::capy::ContextAwaitable`s, and all three produce the identical result +at runtime. This demonstrates that the ContextAwaitable protocol is independent of how asynchronous completion is detected: a host-function callback is only one option, and not the best-scaling one. diff --git a/example/cuda/notification-strategies/notification_strategies.cu b/example/cuda/notification-strategies/notification_strategies.cu index 1753fa101..16f663ea8 100644 --- a/example/cuda/notification-strategies/notification_strategies.cu +++ b/example/cuda/notification-strategies/notification_strategies.cu @@ -9,7 +9,7 @@ // One GPU completion, three notification mechanisms. // -// The IoAwaitable protocol does not care how completion is detected. The +// The ContextAwaitable protocol does not care how completion is detected. The // same pipeline (fill a device buffer, copy it back, await the stream) // runs once per mechanism (host-function callback, event polling, // deferred blocking synchronize) and all three must produce the same @@ -19,7 +19,7 @@ #include "notification_strategies.hpp" #include -#include +#include #include #include @@ -38,10 +38,10 @@ namespace ex = boost::capy::example; static_assert(std::is_same_v< decltype(ex::make_cuda_error(cudaSuccess)), std::error_code>); -// The whole point: three different mechanisms, all IoAwaitables. -static_assert(capy::IoAwaitable); -static_assert(capy::IoAwaitable); -static_assert(capy::IoAwaitable); +// The whole point: three different mechanisms, all ContextAwaitables. +static_assert(capy::ContextAwaitable); +static_assert(capy::ContextAwaitable); +static_assert(capy::ContextAwaitable); namespace { diff --git a/example/cuda/notification-strategies/notification_strategies.hpp b/example/cuda/notification-strategies/notification_strategies.hpp index 81962a5e3..2b202fff1 100644 --- a/example/cuda/notification-strategies/notification_strategies.hpp +++ b/example/cuda/notification-strategies/notification_strategies.hpp @@ -11,8 +11,8 @@ #define BOOST_CAPY_EXAMPLE_CUDA_NOTIFICATION_STRATEGIES_HPP #include +#include #include -#include #include @@ -233,10 +233,10 @@ class cuda_stream return stream_; } - /// Return an IoAwaitable that completes via a host-function callback. + /// Return a ContextAwaitable that completes via a host-function callback. callback_awaitable sync_via_callback() noexcept; - /// Return an IoAwaitable that completes after a blocking synchronize + /// Return a ContextAwaitable that completes after a blocking synchronize /// runs on `svc`. deferred_sync_awaitable sync_via_deferred(sync_service& svc) noexcept; }; @@ -293,12 +293,12 @@ class cuda_event throw std::system_error(make_cuda_error(err)); } - /// Return an IoAwaitable that completes when this event is observed + /// Return a ContextAwaitable that completes when this event is observed /// ready by `svc`. poll_awaitable sync_via_poll(poll_service& svc) noexcept; }; -/** IoAwaitable that resumes via a CUDA host-function callback. +/** ContextAwaitable that resumes via a CUDA host-function callback. `cudaLaunchHostFunc` enqueues a host callback at the stream's current position. CUDA runs it on a driver-owned thread that must not make @@ -340,7 +340,7 @@ struct callback_awaitable } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { cont_.h = h; cont_.owner = env->owner; @@ -376,7 +376,7 @@ cuda_stream::sync_via_callback() noexcept return callback_awaitable{stream_}; } -/// IoAwaitable that resumes when a CUDA event reports ready, detected by +/// ContextAwaitable that resumes when a CUDA event reports ready, detected by /// a polling service thread rather than a callback or a blocking wait. /// /// @note A production awaitable should also check `env->stop_token` for @@ -402,7 +402,7 @@ class poll_awaitable } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { cont_.h = h; cont_.owner = env->owner; @@ -422,7 +422,7 @@ cuda_event::sync_via_poll(poll_service& svc) noexcept return poll_awaitable{svc, event_}; } -/// IoAwaitable that resumes after a blocking `cudaStreamSynchronize` runs +/// ContextAwaitable that resumes after a blocking `cudaStreamSynchronize` runs /// on a service thread, keeping the worker thread free meanwhile. class deferred_sync_awaitable { @@ -444,7 +444,7 @@ class deferred_sync_awaitable } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { auto ex = env->executor; auto stream = stream_; diff --git a/example/cuda/pipeline/README.md b/example/cuda/pipeline/README.md index 6f8ca2cd2..96be2f56d 100644 --- a/example/cuda/pipeline/README.md +++ b/example/cuda/pipeline/README.md @@ -16,7 +16,7 @@ built but not run (P4251R0): `stdexec::upon_error`, and driven by `stdexec::sync_wait`. Two runs: a happy-path read, and a peer-close that exercises the `upon_error` arm. - `read_some` returns `io_result`, which `as_sender` rejects at + `read_some` returns `results`, which `as_sender` rejects at compile time: sender completion channels are exclusive, so routing the error through `set_error` would drop the byte count of a partial read. The scene therefore wraps the read in a `task` diff --git a/example/cuda/pipeline/cuda_pipeline.cu b/example/cuda/pipeline/cuda_pipeline.cu index 9bd22d06b..3ac216af5 100644 --- a/example/cuda/pipeline/cuda_pipeline.cu +++ b/example/cuda/pipeline/cuda_pipeline.cu @@ -215,9 +215,9 @@ run_scene1(capy::thread_pool& pool, float& out) // Scene 2: a stream read exposed as a stdexec sender, composed with // stdexec::upon_error, driven by sync_wait. write_env injects the // capy executor that the as_sender bridge needs to drive the -// underlying IoAwaitable. +// underlying ContextAwaitable. // -// read_some returns io_result, which as_sender rejects at +// read_some returns results, which as_sender rejects at // compile time: sender completion channels are exclusive, so routing // the error through set_error would drop the byte count of a partial // read. The sanctioned route is a task wrapper that moves @@ -245,7 +245,7 @@ scene2_happy_path(capy::thread_pool& pool) auto sndr = ex::write_env( capy::as_sender( read_into(a, capy::mutable_buffer(buf, sizeof buf), n)), - ex::prop{capy::get_io_executor, pool.get_executor()}) + ex::prop{capy::get_context_executor, pool.get_executor()}) | ex::upon_error([](auto e) noexcept { if constexpr (std::is_same_v< std::decay_t, std::error_code>) @@ -281,7 +281,7 @@ scene2_error_path(capy::thread_pool& pool) auto sndr = ex::write_env( capy::as_sender( read_into(a, capy::mutable_buffer(buf, sizeof buf), n)), - ex::prop{capy::get_io_executor, pool.get_executor()}) + ex::prop{capy::get_context_executor, pool.get_executor()}) | ex::upon_error([&](auto e) noexcept { if constexpr (std::is_same_v< std::decay_t, std::error_code>) diff --git a/example/cuda/pipeline/sender_awaitable.hpp b/example/cuda/pipeline/sender_awaitable.hpp index c28f5c546..7b63dc6c2 100644 --- a/example/cuda/pipeline/sender_awaitable.hpp +++ b/example/cuda/pipeline/sender_awaitable.hpp @@ -12,8 +12,8 @@ #define BOOST_CAPY_BENCH_STDEXEC_SENDER_AWAITABLE_HPP #include -#include -#include +#include +#include #include @@ -185,7 +185,7 @@ struct bridge_receiver /** Awaitable that bridges a stdexec sender into a Capy coroutine. - Satisfies IoAwaitable. When co_awaited inside + Satisfies ContextAwaitable. When co_awaited inside a capy::task, connects the sender to a bridge receiver, starts the operation, and resumes the coroutine when the sender completes. @@ -200,7 +200,7 @@ struct bridge_receiver completion signatures at compile time. If the sender can complete with set_error(std::error_code), await_resume - returns io_result so the error code is a + returns results so the error code is a value, not an exception. Otherwise await_resume returns the value directly and genuine exceptions are rethrown. @@ -270,7 +270,7 @@ struct [[nodiscard]] sender_awaitable std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { ::new(op_buf_) op_state_type( stdexec::connect( @@ -304,7 +304,7 @@ struct [[nodiscard]] sender_awaitable private: // Sender can complete with - // set_error(error_code). Return io_result + // set_error(error_code). Return `results` // so the error code is a value, not an // exception. auto await_resume_ec() @@ -320,13 +320,13 @@ struct [[nodiscard]] sender_awaitable { // stopped at index 4 if(result_.index() == 4) - return io_result<>{ + return results<>{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result<>{ + return results<>{ std::get<2>(result_)}; - return io_result<>{}; + return results<>{}; } else if constexpr ( std::tuple_size_v< @@ -335,13 +335,13 @@ struct [[nodiscard]] sender_awaitable using T = std::tuple_element_t< 0, value_tuple>; if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled), T{}}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_), T{}}; - return io_result{ + return results{ std::error_code(), std::get<0>( std::get<1>( @@ -350,13 +350,13 @@ struct [[nodiscard]] sender_awaitable else { if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled), value_tuple{}}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_), value_tuple{}}; - return io_result{ + return results{ std::error_code(), std::get<1>( std::move(result_))}; @@ -392,11 +392,11 @@ struct [[nodiscard]] sender_awaitable } }; -/** Create an IoAwaitable from a stdexec sender. +/** Create a ContextAwaitable from a stdexec sender. If the sender can complete with set_error(std::error_code), the returned - awaitable yields io_result so the error code + awaitable yields results so the error code is a value, not an exception. Otherwise the awaitable yields the value directly. @@ -413,7 +413,7 @@ struct [[nodiscard]] sender_awaitable @endcode @param sndr The sender to bridge. - @return An IoAwaitable that can be co_awaited + @return A ContextAwaitable that can be co_awaited in a capy::task. */ template diff --git a/example/custom-executor/custom_executor.cpp b/example/custom-executor/custom_executor.cpp index 2783b39d3..957bcdfc8 100644 --- a/example/custom-executor/custom_executor.cpp +++ b/example/custom-executor/custom_executor.cpp @@ -159,10 +159,10 @@ run_loop::get_executor() noexcept static_assert(capy::Executor); // end::concept_check[] -capy::io_task compute(int x) +capy::task> compute(int x) { std::cout << " computing " << x << " * " << x << "\n"; - co_return capy::io_result{std::error_code(), x * x}; + co_return capy::results{std::error_code(), x * x}; } capy::task<> run_tasks() diff --git a/example/fabrics/README.md b/example/fabrics/README.md index d1fda9476..81b413bf8 100644 --- a/example/fabrics/README.md +++ b/example/fabrics/README.md @@ -1,6 +1,6 @@ # Fabrics example (P4251R0) -The transport-neutral, non-GPU listings from P4251R0 "IoAwaitables for GPU +The transport-neutral, non-GPU listings from P4251R0 "ContextAwaitables for GPU Data Movement". Validation that the paper's byte-oriented and HPC-fabric calls are type-correct against the real `boost::capy` API and the installed fabric libraries. Nothing here is executed; a clean build is the diff --git a/example/gui-integration/gui_integration.cpp b/example/gui-integration/gui_integration.cpp index 6f2921605..bb5cc1f3e 100644 --- a/example/gui-integration/gui_integration.cpp +++ b/example/gui-integration/gui_integration.cpp @@ -328,12 +328,12 @@ class dialog // end::dialog[] // tag::dialog_awaitable[] -// An IoAwaitable for an operation the toolkit completes. The protocol -// is the subject of the IoAwaitable page; one line of it matters here. +// A ContextAwaitable for an operation the toolkit completes. The protocol +// is the subject of the ContextAwaitable page; one line of it matters here. struct show_dialog { dialog& dialog_; - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_ = {}; std::string answer_ = {}; @@ -343,7 +343,7 @@ struct show_dialog } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { env_ = env; cont_.h = h; diff --git a/example/parallel-fetch/parallel_fetch.cpp b/example/parallel-fetch/parallel_fetch.cpp index c643b62f9..4e101c9c3 100644 --- a/example/parallel-fetch/parallel_fetch.cpp +++ b/example/parallel-fetch/parallel_fetch.cpp @@ -55,8 +55,8 @@ capy::task<> fetch_user_dashboard(std::string username) // Heterogeneous return types are flattened into the result. std::cout << "Starting parallel fetches...\n"; - auto wrap = [](auto inner) -> capy::io_task { - co_return capy::io_result{ + auto wrap = [](auto inner) -> capy::task> { + co_return capy::results{ std::error_code(), co_await std::move(inner)}; }; @@ -74,16 +74,16 @@ capy::task<> fetch_user_dashboard(std::string username) } // Example with void tasks -capy::io_task<> log_access(std::string resource) +capy::task> log_access(std::string resource) { std::cout << "Logging access to: " << resource << "\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; } -capy::io_task<> update_metrics(std::string metric) +capy::task> update_metrics(std::string metric) { std::cout << "Updating metric: " << metric << "\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; } capy::task fetch_with_side_effects() @@ -105,7 +105,7 @@ capy::task fetch_with_side_effects() } // Error handling example -capy::io_task might_fail(bool should_fail, std::string name) +capy::task> might_fail(bool should_fail, std::string name) { std::cout << "Task " << name << " starting\n"; @@ -115,7 +115,7 @@ capy::io_task might_fail(bool should_fail, std::string name) } std::cout << "Task " << name << " completed\n"; - co_return capy::io_result{std::error_code(), 42}; + co_return capy::results{std::error_code(), 42}; } capy::task<> demonstrate_error_handling() diff --git a/example/parallel-tasks/parallel_tasks.cpp b/example/parallel-tasks/parallel_tasks.cpp index 50f0e889b..e4d396e60 100644 --- a/example/parallel-tasks/parallel_tasks.cpp +++ b/example/parallel-tasks/parallel_tasks.cpp @@ -25,7 +25,7 @@ namespace capy = boost::capy; // Sum integers in [lo, hi) -capy::io_task partial_sum(int lo, int hi) +capy::task> partial_sum(int lo, int hi) { std::ostringstream oss; oss << " range [" << lo << ", " << hi @@ -35,7 +35,7 @@ capy::io_task partial_sum(int lo, int hi) long long sum = 0; for (int i = lo; i < hi; ++i) sum += i; - co_return capy::io_result{std::error_code(), sum}; + co_return capy::results{std::error_code(), sum}; } int main() @@ -63,7 +63,7 @@ int main() std::cout << "Dispatching " << num_tasks << " parallel tasks...\n"; - std::vector> tasks; + std::vector>> tasks; for (int i = 0; i < num_tasks; ++i) tasks.push_back(partial_sum(i * chunk, (i + 1) * chunk)); diff --git a/example/producer-consumer/producer_consumer.cpp b/example/producer-consumer/producer_consumer.cpp index 32f56e186..045309ab2 100644 --- a/example/producer-consumer/producer_consumer.cpp +++ b/example/producer-consumer/producer_consumer.cpp @@ -41,22 +41,22 @@ int main() int shared_value = 0; // tag::producer[] - auto producer = [&]() -> capy::io_task<> { + auto producer = [&]() -> capy::task> { std::cout << "Producer: preparing data...\n"; shared_value = 42; std::cout << "Producer: data ready, signaling\n"; data_ready.set(); - co_return capy::io_result<>{}; + co_return capy::results<>{}; }; // end::producer[] // tag::consumer[] - auto consumer = [&]() -> capy::io_task<> { + auto consumer = [&]() -> capy::task> { std::cout << "Consumer: waiting for data...\n"; auto [ec] = co_await data_ready.wait(); (void)ec; std::cout << "Consumer: received value " << shared_value << "\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; }; // end::consumer[] diff --git a/example/sender-bridge/sender_awaitable.hpp b/example/sender-bridge/sender_awaitable.hpp index b6706f821..74872933d 100644 --- a/example/sender-bridge/sender_awaitable.hpp +++ b/example/sender-bridge/sender_awaitable.hpp @@ -12,8 +12,8 @@ #include #include -#include -#include +#include +#include #include @@ -115,7 +115,7 @@ struct bridge_receiver result_variant* result_; continuation cont_; - io_env const* env_; + await_context const* env_; auto get_env() const noexcept -> bridge_env { @@ -176,7 +176,7 @@ struct bridge_receiver /** Awaitable that bridges a beman::execution sender into a Capy coroutine. - Satisfies IoAwaitable. When co_awaited inside + Satisfies ContextAwaitable. When co_awaited inside a capy::task, connects the sender to a bridge receiver, starts the operation, and resumes the coroutine on the caller's executor when @@ -186,7 +186,7 @@ struct bridge_receiver completion signatures at compile time. If the sender can complete with set_error(std::error_code), await_resume - returns io_result so the error code is a + returns results so the error code is a value, not an exception. Otherwise await_resume returns the value directly and genuine exceptions are rethrown. @@ -256,7 +256,7 @@ struct [[nodiscard]] sender_awaitable std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { ::new(op_buf_) op_state_type( beman::execution::connect( @@ -282,7 +282,7 @@ struct [[nodiscard]] sender_awaitable private: // Sender can complete with - // set_error(error_code). Return io_result + // set_error(error_code). Return `results` // so the error code is a value, not an // exception. auto await_resume_ec() @@ -298,13 +298,13 @@ struct [[nodiscard]] sender_awaitable { // stopped at index 4 if(result_.index() == 4) - return io_result<>{ + return results<>{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result<>{ + return results<>{ std::get<2>(result_)}; - return io_result<>{}; + return results<>{}; } else if constexpr ( std::tuple_size_v< @@ -313,13 +313,13 @@ struct [[nodiscard]] sender_awaitable using T = std::tuple_element_t< 0, value_tuple>; if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_)}; - return io_result{ + return results{ std::error_code(), std::get<0>( std::get<1>( @@ -328,13 +328,13 @@ struct [[nodiscard]] sender_awaitable else { if(result_.index() == 4) - return io_result{ + return results{ make_error_code( error::canceled)}; if(result_.index() == 2) - return io_result{ + return results{ std::get<2>(result_)}; - return io_result{ + return results{ std::error_code(), std::get<1>( std::move(result_))}; @@ -370,12 +370,12 @@ struct [[nodiscard]] sender_awaitable } }; -/** Create an IoAwaitable from a +/** Create a ContextAwaitable from a beman::execution sender. If the sender can complete with set_error(std::error_code), the returned - awaitable yields io_result so the error code + awaitable yields results so the error code is a value, not an exception. Otherwise the awaitable yields the value directly. @@ -392,7 +392,7 @@ struct [[nodiscard]] sender_awaitable @endcode @param sndr The sender to bridge. - @return An IoAwaitable that can be co_awaited + @return A ContextAwaitable that can be co_awaited in a capy::task. */ template diff --git a/example/strand-serialization/strand_serialization.cpp b/example/strand-serialization/strand_serialization.cpp index 94f639994..a382b3a97 100644 --- a/example/strand-serialization/strand_serialization.cpp +++ b/example/strand-serialization/strand_serialization.cpp @@ -51,17 +51,17 @@ int main() // Each coroutine increments the shared counter without locks. // The strand ensures only one coroutine runs at a time. - auto increment = [&](int id) -> capy::io_task<> { + auto increment = [&](int id) -> capy::task> { for (int i = 0; i < increments_per_coro; ++i) ++counter; std::cout << "Coroutine " << id << " finished, counter = " << counter << "\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; }; // end::counter[] auto run_all = [&]() -> capy::task<> { - std::vector> tasks; + std::vector>> tasks; for (int i = 0; i < num_coroutines; ++i) tasks.push_back(increment(i)); (void) co_await capy::when_all(std::move(tasks)); diff --git a/example/timeout-cancellation/timeout_cancellation.cpp b/example/timeout-cancellation/timeout_cancellation.cpp index dd7744efe..97ef41703 100644 --- a/example/timeout-cancellation/timeout_cancellation.cpp +++ b/example/timeout-cancellation/timeout_cancellation.cpp @@ -70,15 +70,15 @@ struct fetch_channel // wakes fetch_ready. If the deadline wins first, when_any's stop // request cancels the wait; flag the worker so it stops early. // tag::race_await_fetch[] -capy::io_task await_fetch(fetch_channel& ch) +capy::task> await_fetch(fetch_channel& ch) { auto [ec] = co_await ch.fetch_ready.wait(); if (ec) { ch.cancelled.store(true); - co_return capy::io_result{ec, {}}; + co_return capy::results{ec, {}}; } - co_return capy::io_result{std::error_code(), std::move(ch.result)}; + co_return capy::results{std::error_code(), std::move(ch.result)}; } // end::race_await_fetch[] @@ -86,10 +86,10 @@ capy::io_task await_fetch(fetch_channel& ch) // the waker. This is the escape hatch: the user supplies the // thread and the clock. // tag::race_deadline[] -capy::io_task<> deadline(capy::async_waker& waker) +capy::task> deadline(capy::async_waker& waker) { auto [ec] = co_await waker.wait(); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } // end::race_deadline[] diff --git a/example/when-any-cancellation/when_any_cancellation.cpp b/example/when-any-cancellation/when_any_cancellation.cpp index 98bf00680..41448be86 100644 --- a/example/when-any-cancellation/when_any_cancellation.cpp +++ b/example/when-any-cancellation/when_any_cancellation.cpp @@ -37,7 +37,7 @@ namespace capy = boost::capy; // Simulates a data source that takes `steps` iterations to produce a result. // Each step checks the stop token so the task exits promptly when cancelled. -capy::io_task fetch_from_source( +capy::task> fetch_from_source( std::string name, int steps, int step_ms) { auto token = co_await capy::this_coro::stop_token; @@ -48,7 +48,7 @@ capy::io_task fetch_from_source( { std::cout << " [" << name << "] cancelled at step " << i << "/" << steps << "\n"; - co_return capy::io_result{std::error_code(), name + ": cancelled"}; + co_return capy::results{std::error_code(), name + ": cancelled"}; } // Simulate work @@ -59,7 +59,7 @@ capy::io_task fetch_from_source( << (i + 1) << "/" << steps << "\n"; } - co_return capy::io_result{std::error_code(), name + ": done"}; + co_return capy::results{std::error_code(), name + ": done"}; } // Race three sources — the fastest one wins, the rest get cancelled. @@ -87,7 +87,7 @@ capy::task<> race_data_sources() // A void task that loops until stopped. // Useful for background workers that run indefinitely. -capy::io_task<> background_worker(std::string name, int step_ms) +capy::task> background_worker(std::string name, int step_ms) { auto token = co_await capy::this_coro::stop_token; int iteration = 0; @@ -102,16 +102,16 @@ capy::io_task<> background_worker(std::string name, int step_ms) std::cout << " [" << name << "] stopped after " << iteration << " iterations\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; } // A task that finishes after a fixed delay (acts as a timeout). -capy::io_task<> timeout(int ms) +capy::task> timeout(int ms) { std::this_thread::sleep_for( std::chrono::milliseconds(ms)); std::cout << " [timeout] expired after " << ms << "ms\n"; - co_return capy::io_result<>{}; + co_return capy::results<>{}; } // Race three replicas using variadic overload. diff --git a/include/boost/capy.hpp b/include/boost/capy.hpp index f37fdf991..a27e2caab 100644 --- a/include/boost/capy.hpp +++ b/include/boost/capy.hpp @@ -20,9 +20,8 @@ // Core types #include #include -#include -#include #include +#include #include // Algorithms @@ -48,8 +47,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -66,7 +65,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/boost/capy/concept/io_awaitable.hpp b/include/boost/capy/concept/context_awaitable.hpp similarity index 84% rename from include/boost/capy/concept/io_awaitable.hpp rename to include/boost/capy/concept/context_awaitable.hpp index 1c32c0c68..02de16529 100644 --- a/include/boost/capy/concept/io_awaitable.hpp +++ b/include/boost/capy/concept/context_awaitable.hpp @@ -8,11 +8,11 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_CONCEPT_IO_AWAITABLE_HPP -#define BOOST_CAPY_CONCEPT_IO_AWAITABLE_HPP +#ifndef BOOST_CAPY_CONCEPT_CONTEXT_AWAITABLE_HPP +#define BOOST_CAPY_CONCEPT_CONTEXT_AWAITABLE_HPP #include -#include +#include #include #include #include @@ -26,16 +26,16 @@ namespace capy { In a `co_await` expression the language accepts exactly three return types from `await_suspend`: `void`, `bool`, and `std::coroutine_handle

` for any promise type `P`. This concept - expresses that rule so that @ref IoAwaitable can reject a malformed + expresses that rule so that @ref ContextAwaitable can reject a malformed awaitable at constraint-checking time rather than deep inside the coroutine machinery. - See the description of `await_suspend` in @ref IoAwaitable for the + See the description of `await_suspend` in @ref ContextAwaitable for the meaning attached to each return type. @tparam T The return type of `await_suspend`. - @see @ref IoAwaitable + @see @ref ContextAwaitable */ template concept AwaitSuspendResult = @@ -51,7 +51,7 @@ concept AwaitSuspendResult = /** Describes types that can be `co_await`-ed in Capy-coroutines and that can be passed the information about the execution environment. - See the tutorial section _The IoAwaitable Protocol_ for the description + See the tutorial section _The ContextAwaitable Protocol_ for the description of the execution environment propagation mechanism in Capy. @@ -114,7 +114,7 @@ concept AwaitSuspendResult = the chain from its owner. A continuation without an owner is destroyed alone, leaving the frames above it holding a destroyed frame. - _Lifetime_: The object of type @ref io_env pointed to by `env` remains valid + _Lifetime_: The object of type @ref await_context pointed to by `env` remains valid for the duration of the async operation represented by `A`. This is guarantee is maintained by @ref run, @ref run_async and the other functions that "start a task". @@ -142,17 +142,17 @@ concept AwaitSuspendResult = @par Models - General-purpose class templates that model `IoAwaitable`: @ref task, @ref quitter, @ref immediate. + General-purpose class templates that model `ContextAwaitable`: @ref task, @ref quitter, @ref immediate. - @see @ref AwaitSuspendResult, @ref IoRunnable, @ref io_env, @ref executor_ref + @see @ref AwaitSuspendResult, @ref ContextRunnable, @ref await_context, @ref executor_ref */ template -concept IoAwaitable = std::move_constructible && +concept ContextAwaitable = std::move_constructible && requires( A a, std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { { a.await_ready() } -> std::same_as; { a.await_suspend(h, env) } -> AwaitSuspendResult; @@ -168,18 +168,18 @@ concept IoAwaitable = std::move_constructible && template using awaitable_result_t = decltype(std::declval&>().await_resume()); -/** Requires a sized input range whose value type satisfies `IoAwaitable`. +/** Requires a sized input range whose value type satisfies `ContextAwaitable`. - A range satisfies `IoAwaitableRange` if it is a sized input range - whose value type satisfies @ref IoAwaitable. + A range satisfies `ContextAwaitableRange` if it is a sized input range + whose value type satisfies @ref ContextAwaitable. @tparam R The range type. */ template -concept IoAwaitableRange = +concept ContextAwaitableRange = std::ranges::input_range && std::ranges::sized_range && - IoAwaitable>; + ContextAwaitable>; } // namespace capy } // namespace boost diff --git a/include/boost/capy/concept/io_runnable.hpp b/include/boost/capy/concept/context_runnable.hpp similarity index 81% rename from include/boost/capy/concept/io_runnable.hpp rename to include/boost/capy/concept/context_runnable.hpp index abd086137..af3fc8308 100644 --- a/include/boost/capy/concept/io_runnable.hpp +++ b/include/boost/capy/concept/context_runnable.hpp @@ -8,12 +8,12 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_CONCEPT_IO_RUNNABLE_HPP -#define BOOST_CAPY_CONCEPT_IO_RUNNABLE_HPP +#ifndef BOOST_CAPY_CONCEPT_CONTEXT_RUNNABLE_HPP +#define BOOST_CAPY_CONCEPT_CONTEXT_RUNNABLE_HPP #include -#include -#include +#include +#include #include #include @@ -22,9 +22,9 @@ namespace boost { namespace capy { -/** Requires an `IoAwaitable` that exposes `handle` and `release`, and whose promise takes a continuation and an environment from outside a coroutine. +/** Requires a `ContextAwaitable` that exposes `handle` and `release`, and whose promise takes a continuation and an environment from outside a coroutine. - Extends @ref IoAwaitable with the operations that @ref run and + Extends @ref ContextAwaitable with the operations that @ref run and @ref run_async need. Those operations start a task, transfer ownership of the coroutine frame, and retrieve results or exceptions after completion. @@ -33,7 +33,7 @@ namespace capy { @par Syntactic Requirements - @li `T` must satisfy @ref IoAwaitable. + @li `T` must satisfy @ref ContextAwaitable. @li `T::promise_type` must be a valid type. @li `ct.handle()` (callable on a `const` task) returns `std::coroutine_handle`, must be `noexcept`. @@ -75,9 +75,9 @@ namespace capy { The `set_environment` operation establishes the execution environment: - @li Sets the `io_env` pointer that propagates executor, stop token, + @li Sets the `await_context` pointer that propagates executor, stop token, and allocator through the coroutine chain. - @li The pointed-to `io_env` must outlive the coroutine. + @li The pointed-to `await_context` must outlive the coroutine. @par Requirements @@ -97,12 +97,12 @@ namespace capy { @par !example example - @see IoAwaitable, run, run_async + @see ContextAwaitable, run, run_async */ -// tag::io_runnable_concept[] +// tag::context_runnable_concept[] template -concept IoRunnable = - IoAwaitable && +concept ContextRunnable = + ContextAwaitable && requires { typename T::promise_type; } && requires(T& t, T const& ct, typename T::promise_type const& cp, typename T::promise_type& p) { @@ -110,13 +110,13 @@ concept IoRunnable = { cp.exception() } noexcept -> std::same_as; { t.release() } noexcept; { p.set_continuation(std::coroutine_handle<>{}) } noexcept; - { p.set_environment(static_cast(nullptr)) } noexcept; + { p.set_environment(static_cast(nullptr)) } noexcept; } && (std::is_void_v().await_resume())> || requires(typename T::promise_type& p) { p.result(); }); -// end::io_runnable_concept[] +// end::context_runnable_concept[] } // namespace capy } // namespace boost diff --git a/include/boost/capy/concept/read_stream.hpp b/include/boost/capy/concept/read_stream.hpp index d2b05f236..6524dac11 100644 --- a/include/boost/capy/concept/read_stream.hpp +++ b/include/boost/capy/concept/read_stream.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -23,7 +23,7 @@ namespace boost { namespace capy { -/** Requires `read_some` to return an `IoAwaitable` decomposing to an error code and a size. +/** Requires `read_some` to return a `ContextAwaitable` decomposing to an error code and a size. A type satisfies `ReadStream` if it provides a `read_some` member function template that accepts any @ref MutableBufferSequence @@ -32,7 +32,7 @@ namespace capy { @par Syntactic Requirements @li `T` must provide a `read_some` member function template accepting any @ref MutableBufferSequence. - @li The return type of `read_some` must satisfy @ref IoAwaitable. + @li The return type of `read_some` must satisfy @ref ContextAwaitable. @li The awaitable's result must decompose to `(error_code,std::size_t)` via structured bindings. @@ -102,14 +102,14 @@ namespace capy { @par !example example_2 - @see IoAwaitable, MutableBufferSequence, awaitable_decomposes_to + @see ContextAwaitable, MutableBufferSequence, awaitable_decomposes_to */ // tag::read_stream_concept[] template concept ReadStream = requires(T& stream, mutable_buffer_archetype buffers) { - { stream.read_some(buffers) } -> IoAwaitable; + { stream.read_some(buffers) } -> ContextAwaitable; requires awaitable_decomposes_to< decltype(stream.read_some(buffers)), std::error_code, std::size_t>; diff --git a/include/boost/capy/concept/write_stream.hpp b/include/boost/capy/concept/write_stream.hpp index fc3b3fbcc..dc8390be8 100644 --- a/include/boost/capy/concept/write_stream.hpp +++ b/include/boost/capy/concept/write_stream.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -23,7 +23,7 @@ namespace boost { namespace capy { -/** Requires `write_some` to return an `IoAwaitable` decomposing to an error code and a size. +/** Requires `write_some` to return a `ContextAwaitable` decomposing to an error code and a size. A type satisfies `WriteStream` if it provides a `write_some` member function template that accepts any @ref ConstBufferSequence @@ -35,7 +35,7 @@ namespace capy { @li `T` must provide a `write_some` member function template accepting any @ref ConstBufferSequence. - @li The return type of `write_some` must satisfy @ref IoAwaitable. + @li The return type of `write_some` must satisfy @ref ContextAwaitable. @li The awaitable's result must decompose to `(error_code,std::size_t)` via structured bindings. @@ -111,14 +111,14 @@ namespace capy { @par !example example_2 - @see IoAwaitable, ConstBufferSequence, awaitable_decomposes_to + @see ContextAwaitable, ConstBufferSequence, awaitable_decomposes_to */ // tag::write_stream_concept[] template concept WriteStream = requires(T& stream, const_buffer_archetype buffers) { - { stream.write_some(buffers) } -> IoAwaitable; + { stream.write_some(buffers) } -> ContextAwaitable; requires awaitable_decomposes_to< decltype(stream.write_some(buffers)), std::error_code, std::size_t>; diff --git a/include/boost/capy/continuation.hpp b/include/boost/capy/continuation.hpp index 7ce7e0859..a478b73aa 100644 --- a/include/boost/capy/continuation.hpp +++ b/include/boost/capy/continuation.hpp @@ -101,7 +101,7 @@ struct continuation /** The owner of the chain segment the coroutine belongs to. - Set from the coroutine's @ref io_env when it is parked or + Set from the coroutine's @ref await_context when it is parked or queued. A holder that gives the coroutine up passes this continuation to @ref abandon, which unwinds the segment from its owner. Null for a coroutine with no owner, such as an owner's own diff --git a/include/boost/capy/detail/abandonable.hpp b/include/boost/capy/detail/abandonable.hpp index 472c60524..d70846896 100644 --- a/include/boost/capy/detail/abandonable.hpp +++ b/include/boost/capy/detail/abandonable.hpp @@ -12,7 +12,7 @@ #include #include -#include +#include #include #include @@ -44,7 +44,7 @@ struct abandonable } template - auto await_suspend(H h, io_env const* env) + auto await_suspend(H h, await_context const* env) { owner_ = env->owner; return a_.await_suspend(h, env); diff --git a/include/boost/capy/detail/await_suspend_helper.hpp b/include/boost/capy/detail/await_suspend_helper.hpp index c2d704ccb..35837254a 100644 --- a/include/boost/capy/detail/await_suspend_helper.hpp +++ b/include/boost/capy/detail/await_suspend_helper.hpp @@ -12,7 +12,7 @@ #define BOOST_CAPY_DETAIL_AWAIT_SUSPEND_HELPER_HPP #include -#include +#include #include @@ -73,7 +73,7 @@ template std::coroutine_handle<> call_await_suspend( Awaitable* a, std::coroutine_handle<> h, - io_env const* env) + await_context const* env) { using R = decltype(a->await_suspend(h, env)); if constexpr (std::is_void_v) diff --git a/include/boost/capy/detail/io_result_combinators.hpp b/include/boost/capy/detail/io_result_combinators.hpp deleted file mode 100644 index bd92ce82a..000000000 --- a/include/boost/capy/detail/io_result_combinators.hpp +++ /dev/null @@ -1,133 +0,0 @@ -// -// Copyright (c) 2026 Steve Gerbino -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/capy -// - -#ifndef BOOST_CAPY_DETAIL_IO_RESULT_COMBINATORS_HPP -#define BOOST_CAPY_DETAIL_IO_RESULT_COMBINATORS_HPP - -#include -#include - -#include -#include -#include -#include - -namespace boost { -namespace capy { -namespace detail { - -/// True when every awaitable in the pack returns an io_result. -template -concept all_io_result_awaitables = - (is_io_result_v> && ...); - -/// True when the io_result-aware when_all overload should be used. -template -concept when_all_io_eligible = - (sizeof...(As) > 0) - && all_io_result_awaitables; - -/// True when the io_result-aware when_any overload should be used. -template -concept when_any_io_eligible = - (sizeof...(As) > 0) - && all_io_result_awaitables; - -/// Map an io_result specialization to its contributed payload type. -/// -/// io_result -> T (unwrap single) -/// io_result -> tuple (zero, two, or more) -template -struct io_result_payload; - -template -struct io_result_payload> -{ - using type = T; -}; - -template -struct io_result_payload> -{ - using type = std::tuple; -}; - -template -using io_result_payload_t = - typename io_result_payload::type; - -/// Extract the payload value(s) from an io_result, -/// matching the type produced by io_result_payload_t. -template -T -extract_io_payload(io_result&& r) -{ - return std::get<1>(std::move(r)); -} - -template -std::tuple -extract_io_payload(io_result&& r) -{ - return [&](std::index_sequence) { - return std::tuple(std::get(std::move(r))...); - }(std::index_sequence_for{}); -} - -/// Reconstruct a success io_result from a payload extracted by when_any. -template -struct io_result_from_payload; - -template -struct io_result_from_payload> -{ - static io_result apply(T t) - { - return io_result{std::error_code(), std::move(t)}; - } -}; - -template -struct io_result_from_payload> -{ - static io_result apply(std::tuple t) - { - return std::tuple_cat( - std::tuple(), std::move(t)); - } -}; - -/// Build the outer io_result for when_all from a tuple of child io_results. -template -ResultType -build_when_all_io_result_impl(Tuple&& results, std::index_sequence) -{ - std::error_code ec; - (void)((std::get<0>(std::get(results)) && !ec - ? (ec = std::get<0>(std::get(results)), true) - : false) || ...); - - return ResultType{ec, extract_io_payload( - std::move(std::get(results)))...}; -} - -template -ResultType -build_when_all_io_result(std::tuple&& results) -{ - return build_when_all_io_result_impl( - std::move(results), - std::index_sequence_for{}); -} - -} // namespace detail -} // namespace capy -} // namespace boost - -#endif diff --git a/include/boost/capy/detail/results_combinators.hpp b/include/boost/capy/detail/results_combinators.hpp new file mode 100644 index 000000000..c87a0ef66 --- /dev/null +++ b/include/boost/capy/detail/results_combinators.hpp @@ -0,0 +1,133 @@ +// +// Copyright (c) 2026 Steve Gerbino +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// Official repository: https://github.com/cppalliance/capy +// + +#ifndef BOOST_CAPY_DETAIL_RESULTS_COMBINATORS_HPP +#define BOOST_CAPY_DETAIL_RESULTS_COMBINATORS_HPP + +#include +#include + +#include +#include +#include +#include + +namespace boost { +namespace capy { +namespace detail { + +/// True when every awaitable in the pack returns a `results`. +template +concept all_results_awaitables = + (is_results_v> && ...); + +/// True when the `results`-aware when_all overload should be used. +template +concept when_all_results_eligible = + (sizeof...(As) > 0) + && all_results_awaitables; + +/// True when the `results`-aware when_any overload should be used. +template +concept when_any_results_eligible = + (sizeof...(As) > 0) + && all_results_awaitables; + +/// Map a `results` specialization to its contributed payload type. +/// +/// results -> T (unwrap single) +/// results -> tuple (zero, two, or more) +template +struct results_payload; + +template +struct results_payload> +{ + using type = T; +}; + +template +struct results_payload> +{ + using type = std::tuple; +}; + +template +using results_payload_t = + typename results_payload::type; + +/// Extract the payload value(s) from a `results`, +/// matching the type produced by results_payload_t. +template +T +extract_results_payload(results&& r) +{ + return std::get<1>(std::move(r)); +} + +template +std::tuple +extract_results_payload(results&& r) +{ + return [&](std::index_sequence) { + return std::tuple(std::get(std::move(r))...); + }(std::index_sequence_for{}); +} + +/// Reconstruct a success `results` from a payload extracted by when_any. +template +struct results_from_payload; + +template +struct results_from_payload> +{ + static results apply(T t) + { + return results{std::error_code(), std::move(t)}; + } +}; + +template +struct results_from_payload> +{ + static results apply(std::tuple t) + { + return std::tuple_cat( + std::tuple(), std::move(t)); + } +}; + +/// Build the outer `results` for when_all from a tuple of child `results`. +template +ResultType +build_when_all_results_impl(Tuple&& results, std::index_sequence) +{ + std::error_code ec; + (void)((std::get<0>(std::get(results)) && !ec + ? (ec = std::get<0>(std::get(results)), true) + : false) || ...); + + return ResultType{ec, extract_results_payload( + std::move(std::get(results)))...}; +} + +template +ResultType +build_when_all_results(std::tuple&& results) +{ + return build_when_all_results_impl( + std::move(results), + std::index_sequence_for{}); +} + +} // namespace detail +} // namespace capy +} // namespace boost + +#endif diff --git a/include/boost/capy/ex/abandon.hpp b/include/boost/capy/ex/abandon.hpp index 062cc63fe..65ba87332 100644 --- a/include/boost/capy/ex/abandon.hpp +++ b/include/boost/capy/ex/abandon.hpp @@ -27,7 +27,7 @@ namespace capy { on another executor, or one branch of `when_all` or `when_any`. Its owner is the coroutine that started it, suspended above the segment's top frame until that frame completes. Every coroutine in - the segment reaches the owner through its @ref io_env. + the segment reaches the owner through its @ref await_context. When the holder of a suspended coroutine in the segment gives it up with @ref abandon, the owner posts its own continuation to its diff --git a/include/boost/capy/ex/async_event.hpp b/include/boost/capy/ex/async_event.hpp index d57fa84b7..853c3fdf3 100644 --- a/include/boost/capy/ex/async_event.hpp +++ b/include/boost/capy/ex/async_event.hpp @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include @@ -234,7 +234,7 @@ class async_event /** Enqueue the awaiting coroutine until the event is set. - This is the @ref IoAwaitable overload of `await_suspend`. + This is the @ref ContextAwaitable overload of `await_suspend`. If a stop request is already pending on `env->stop_token`, the awaiter records the cancellation and does not enqueue. @@ -263,7 +263,7 @@ class async_event std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { if(env->stop_token.stop_requested()) { @@ -288,10 +288,10 @@ class async_event unlinks it. `set()` pops every waiter, so a canceled waiter may or may not still be linked when it resumes. - @return An empty `io_result<>` if the event was set, or one + @return An empty `results<>` if the event was set, or one holding `error::canceled` if the stop token fired first. */ - [[nodiscard]] io_result<> await_resume() noexcept + [[nodiscard]] results<> await_resume() noexcept { if(active_) { diff --git a/include/boost/capy/ex/async_mutex.hpp b/include/boost/capy/ex/async_mutex.hpp index 43973d89e..2d8a886c2 100644 --- a/include/boost/capy/ex/async_mutex.hpp +++ b/include/boost/capy/ex/async_mutex.hpp @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include @@ -320,7 +320,7 @@ class async_mutex /** Enqueue the awaiting coroutine until the mutex is released. - This is the @ref IoAwaitable overload of `await_suspend`. + This is the @ref ContextAwaitable overload of `await_suspend`. If a stop request is already pending on `env->stop_token`, the awaiter records the cancellation and does not enqueue. The @@ -347,7 +347,7 @@ class async_mutex std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { if(env->stop_token.stop_requested()) { @@ -370,12 +370,12 @@ class async_mutex Destroys the stop callback if one is registered, and unlinks a canceled awaiter from the wait queue. - @return An empty `io_result<>` if the mutex is now held by the + @return An empty `results<>` if the mutex is now held by the awaiting coroutine. Otherwise one holding `error::canceled`, which means the stop token won the race or the mutex was destroyed, and the mutex is not held. */ - [[nodiscard]] io_result<> await_resume() noexcept + [[nodiscard]] results<> await_resume() noexcept { if(std::exchange(granted_, false)) m_->granted_to_ = nullptr; @@ -518,7 +518,7 @@ class async_mutex /** Enqueue the awaiting coroutine until the mutex is released. - This is the @ref IoAwaitable overload of `await_suspend`. It + This is the @ref ContextAwaitable overload of `await_suspend`. It delegates to @ref lock_awaiter::await_suspend on the wrapped awaiter, so it has that function's contract. @@ -537,19 +537,19 @@ class async_mutex std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { return inner_.await_suspend(h, env); } /** Complete the acquisition and report the outcome. - @return An `io_result` destructuring as + @return A `results` destructuring as `[ec, guard]`. On success `ec` is empty and `guard` holds the mutex, releasing it when destroyed. If the wait was canceled, `ec` is `error::canceled` and `guard` holds no mutex. */ - [[nodiscard]] io_result await_resume() noexcept + [[nodiscard]] results await_resume() noexcept { auto r = inner_.await_resume(); if(std::get<0>(r)) diff --git a/include/boost/capy/ex/async_waker.hpp b/include/boost/capy/ex/async_waker.hpp index 47e25fcbd..e6cf21cd0 100644 --- a/include/boost/capy/ex/async_waker.hpp +++ b/include/boost/capy/ex/async_waker.hpp @@ -15,8 +15,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -299,7 +299,7 @@ class async_waker /** Arm the waker with the awaiting coroutine. - This is the @ref IoAwaitable overload of `await_suspend`. + This is the @ref ContextAwaitable overload of `await_suspend`. Unlike `async_event` and `async_mutex`, it has three outcomes, because a `wake()` from another thread can land in the window between `await_ready` and this call. @@ -337,7 +337,7 @@ class async_waker std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { if(env->stop_token.stop_requested()) { @@ -362,6 +362,7 @@ class async_waker std::memory_order_acq_rel, std::memory_order_acquire)) { + // LCOV_EXCL_START race: a wake latched between await_ready and here // Single-waiter precondition: a second concurrent // wait would find the slot armed. BOOST_CAPY_ASSERT(expected == state_token); @@ -374,6 +375,7 @@ class async_waker waker_->st_.store( state_empty, std::memory_order_release); return h; + // LCOV_EXCL_STOP } // While arming nothing else can claim this waiter, so a stop @@ -397,12 +399,12 @@ class async_waker armed bookkeeping, so the destructor does not deregister a slot the resumption already consumed. - @return An empty `io_result<>` if the wait was woken, whether by + @return An empty `results<>` if the wait was woken, whether by `wake()` or by a token consumed inline. Otherwise one holding `error::canceled`, which means the stop token won the race or the waker was destroyed. */ - [[nodiscard]] io_result<> await_resume() noexcept + [[nodiscard]] results<> await_resume() noexcept { if(active_) { @@ -488,7 +490,7 @@ class async_waker No other coroutine is currently waiting on this object; a coroutine woken or canceled but not yet resumed still is. - @return An awaitable that await-returns `io_result<>`; + @return An awaitable that await-returns `results<>`; empty on wakeup, `error::canceled` if the stop token wins. */ @@ -520,14 +522,14 @@ class async_waker std::memory_order_acq_rel, std::memory_order_acquire)) return; - continue; + continue; // LCOV_EXCL_LINE spurious weak CAS failure } // The waiter is still publishing itself; it settles in a // few instructions. if(s == state_arming) { - std::this_thread::yield(); - continue; + std::this_thread::yield(); // LCOV_EXCL_LINE race with a waiter still publishing itself + continue; // LCOV_EXCL_LINE race with a waiter still publishing itself } // armed: winning this CAS claims the waiter, whose // frame is pinned until we post its resumption. @@ -547,7 +549,7 @@ class async_waker w->ex_.post(w->cont_); return; } - } + } // LCOV_EXCL_LINE armed CAS lost to a stop callback or failed spuriously } }; diff --git a/include/boost/capy/ex/io_env.hpp b/include/boost/capy/ex/await_context.hpp similarity index 86% rename from include/boost/capy/ex/io_env.hpp rename to include/boost/capy/ex/await_context.hpp index ff7d7ec28..6844ed759 100644 --- a/include/boost/capy/ex/io_env.hpp +++ b/include/boost/capy/ex/await_context.hpp @@ -8,8 +8,8 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_IO_ENV_HPP -#define BOOST_CAPY_IO_ENV_HPP +#ifndef BOOST_CAPY_AWAIT_CONTEXT_HPP +#define BOOST_CAPY_AWAIT_CONTEXT_HPP #include #include @@ -26,24 +26,24 @@ class chain_owner; /** Carries the executor, stop token, and frame allocator through a coroutine chain. This struct bundles the execution context passed through - coroutine chains via the IoAwaitable protocol. It contains + coroutine chains via the ContextAwaitable protocol. It contains the executor for resumption, a stop token for cancellation, and an optional frame allocator for coroutine frame allocation. @par Lifetime - Launcher functions (@ref run_async, @ref run) own the `io_env` and + Launcher functions (@ref run_async, @ref run) own the `await_context` and guarantee it outlives all tasks and awaitables in the started - chain. Awaitables receive `io_env const*` in `await_suspend` + chain. Awaitables receive `await_context const*` in `await_suspend` and should store it directly, never copy the pointed-to object. @par Thread Safety The referenced executor and allocator must remain valid for the lifetime of any coroutine using this environment. - @see IoAwaitable, IoRunnable + @see ContextAwaitable, ContextRunnable */ -struct io_env +struct await_context { /** The executor for coroutine resumption. */ executor_ref executor; diff --git a/include/boost/capy/ex/io_awaitable_promise_base.hpp b/include/boost/capy/ex/context_awaitable_promise_base.hpp similarity index 92% rename from include/boost/capy/ex/io_awaitable_promise_base.hpp rename to include/boost/capy/ex/context_awaitable_promise_base.hpp index ffcc7df68..28507561c 100644 --- a/include/boost/capy/ex/io_awaitable_promise_base.hpp +++ b/include/boost/capy/ex/context_awaitable_promise_base.hpp @@ -8,14 +8,14 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP -#define BOOST_CAPY_EX_IO_AWAITABLE_PROMISE_BASE_HPP +#ifndef BOOST_CAPY_EX_CONTEXT_AWAITABLE_PROMISE_BASE_HPP +#define BOOST_CAPY_EX_CONTEXT_AWAITABLE_PROMISE_BASE_HPP #include #include +#include #include #include -#include #include #include @@ -33,7 +33,7 @@ namespace capy { 1. **Frame allocation** — The mixin provides `operator new/delete` that use the thread-local frame allocator set by `run_async`. - 2. **Environment storage** — The mixin stores a pointer to the `io_env` + 2. **Environment storage** — The mixin stores a pointer to the `await_context` containing the executor, stop token, and allocator for this coroutine. 3. **Environment access** — Coroutine code can retrieve the environment @@ -63,11 +63,11 @@ namespace capy { @ref this_coro::stop_token_tag, @ref this_coro::frame_allocator_tag), then delegates all other awaitables to your `transform_awaitable`. - @par Making Your Coroutine an IoAwaitable + @par Making Your Coroutine a ContextAwaitable The mixin handles the "inside the coroutine" part—accessing the environment. To receive the environment when your coroutine is awaited - (satisfying @ref IoAwaitable), implement the `await_suspend` overload + (satisfying @ref ContextAwaitable), implement the `await_suspend` overload on your coroutine return type: @par !example example_3 @@ -98,14 +98,14 @@ namespace capy { @see this_coro::environment, this_coro::executor, this_coro::stop_token, this_coro::frame_allocator - @see io_env - @see IoAwaitable + @see await_context + @see ContextAwaitable */ template -class io_awaitable_promise_base +class context_awaitable_promise_base : public frame_alloc_mixin { - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; mutable std::coroutine_handle<> cont_{std::noop_coroutine()}; public: @@ -147,11 +147,11 @@ class io_awaitable_promise_base Call this from your coroutine type's `await_suspend` overload to make the environment available via `co_await this_coro::environment`. The pointed-to - `io_env` must outlive this coroutine. + `await_context` must outlive this coroutine. @param env The environment to store. */ - void set_environment(io_env const* env) noexcept + void set_environment(await_context const* env) noexcept { env_ = env; } @@ -160,7 +160,7 @@ class io_awaitable_promise_base @return The environment. */ - io_env const* environment() const noexcept + await_context const* environment() const noexcept { BOOST_CAPY_ASSERT(env_); return env_; @@ -204,10 +204,10 @@ class io_awaitable_promise_base BOOST_CAPY_ASSERT(env_); struct awaiter { - io_env const* env_; + await_context const* env_; bool await_ready() const noexcept { return true; } void await_suspend(std::coroutine_handle<>) const noexcept { } - io_env const* await_resume() const noexcept { return env_; } + await_context const* await_resume() const noexcept { return env_; } }; return awaiter{env_}; } diff --git a/include/boost/capy/ex/frame_alloc_mixin.hpp b/include/boost/capy/ex/frame_alloc_mixin.hpp index c4f1bea94..941b5657e 100644 --- a/include/boost/capy/ex/frame_alloc_mixin.hpp +++ b/include/boost/capy/ex/frame_alloc_mixin.hpp @@ -36,9 +36,9 @@ namespace capy { and deallocation. This is the same allocation strategy used by @ref - io_awaitable_promise_base. Use this mixin directly when your + context_awaitable_promise_base. Use this mixin directly when your promise type does not need the full environment and continuation - support that `io_awaitable_promise_base` provides. + support that `context_awaitable_promise_base` provides. @par Example @par !example example @@ -48,7 +48,7 @@ namespace capy { The allocation fast path uses thread-local storage and requires no synchronization. The global pool fallback is mutex-protected. - @see io_awaitable_promise_base, frame_allocator, recycling_memory_resource + @see context_awaitable_promise_base, frame_allocator, recycling_memory_resource */ struct frame_alloc_mixin { diff --git a/include/boost/capy/ex/frame_allocator.hpp b/include/boost/capy/ex/frame_allocator.hpp index e82e08063..34affe4c3 100644 --- a/include/boost/capy/ex/frame_allocator.hpp +++ b/include/boost/capy/ex/frame_allocator.hpp @@ -76,7 +76,7 @@ current_frame_allocator_ref() noexcept /** Return the current frame allocator for this thread. These accessors exist to implement the allocator - propagation portion of the @ref IoAwaitable protocol. + propagation portion of the @ref ContextAwaitable protocol. Launcher functions (`run_async`, `run`) set the thread-local value before invoking a child coroutine. The child's `promise_type::operator new` reads it to @@ -88,7 +88,7 @@ current_frame_allocator_ref() noexcept that TLS contains the allocator associated with the currently running chain. Outside that window the value is indeterminate. Only code that implements an - @ref IoAwaitable should call these functions. + @ref ContextAwaitable should call these functions. A return value of `nullptr` means "not specified" - no allocator is established for this chain. @@ -105,7 +105,7 @@ current_frame_allocator_ref() noexcept @return The thread-local memory_resource pointer, or `nullptr` if none is set. - @see set_current_frame_allocator, IoAwaitable + @see set_current_frame_allocator, ContextAwaitable */ inline std::pmr::memory_resource* @@ -119,7 +119,7 @@ get_current_frame_allocator() noexcept Installs @p mr as the frame allocator read by the next coroutine's `promise_type::operator new` on this thread. Only launcher functions and - @ref IoAwaitable machinery should call this; see + @ref ContextAwaitable machinery should call this; see @ref get_current_frame_allocator for the full protocol description. @@ -129,7 +129,7 @@ get_current_frame_allocator() noexcept @param mr The memory_resource to install, or `nullptr` to clear. - @see get_current_frame_allocator, IoAwaitable + @see get_current_frame_allocator, ContextAwaitable */ inline void set_current_frame_allocator( diff --git a/include/boost/capy/ex/immediate.hpp b/include/boost/capy/ex/immediate.hpp index 9ffd96c60..baa17b3cf 100644 --- a/include/boost/capy/ex/immediate.hpp +++ b/include/boost/capy/ex/immediate.hpp @@ -12,8 +12,8 @@ #define BOOST_CAPY_EX_IMMEDIATE_HPP #include -#include -#include +#include +#include #include #include @@ -30,7 +30,7 @@ namespace capy { is optimized away by the compiler. Use this to adapt synchronous operations to satisfy async concepts - like @ref IoAwaitable without the overhead of a full coroutine frame. + like @ref ContextAwaitable without the overhead of a full coroutine frame. @tparam T The result type to wrap. @@ -42,7 +42,7 @@ namespace capy { @par !example example_2 - @see ready, io_result + @see ready, results */ template struct immediate @@ -61,9 +61,9 @@ struct immediate return true; } - /** IoAwaitable protocol overload. + /** ContextAwaitable protocol overload. - This overload allows `immediate` to satisfy the @ref IoAwaitable + This overload allows `immediate` to satisfy the @ref ContextAwaitable concept. Since the result is already available, the environment is unused. @@ -75,7 +75,7 @@ struct immediate std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) const noexcept + await_context const* env) const noexcept { (void)h; (void)env; @@ -104,71 +104,71 @@ struct immediate } }; -/** Create an immediate awaitable for a successful io_result. +/** Create an immediate awaitable for a successful `results`. - This helper creates an @ref immediate wrapping an @ref io_result + This helper creates an @ref immediate wrapping an @ref results with no error and the provided values. @par Example @par !example example_1 - @return An immediate awaitable containing a successful io_result. + @return An immediate awaitable containing a successful `results`. - @see immediate, io_result + @see immediate, results */ inline -immediate> +immediate> ready() noexcept { return {{}}; } -/** Create an immediate awaitable for a successful io_result with one value. +/** Create an immediate awaitable for a successful `results` with one value. @param t1 The result value. - @return An immediate awaitable containing `io_result{std::error_code(), t1}`. + @return An immediate awaitable containing `results{std::error_code(), t1}`. */ template -immediate> +immediate> ready(T1 t1) { return {{std::error_code(), std::move(t1)}}; } -/** Create an immediate awaitable for a successful io_result with two values. +/** Create an immediate awaitable for a successful `results` with two values. @param t1 The first result value. @param t2 The second result value. - @return An immediate awaitable containing `io_result{std::error_code(), t1, t2}`. + @return An immediate awaitable containing `results{std::error_code(), t1, t2}`. */ template -immediate> +immediate> ready(T1 t1, T2 t2) { return {{std::error_code(), std::move(t1), std::move(t2)}}; } -/** Create an immediate awaitable for a successful io_result with three values. +/** Create an immediate awaitable for a successful `results` with three values. @param t1 The first result value. @param t2 The second result value. @param t3 The third result value. - @return An immediate awaitable containing `io_result{std::error_code(), t1, t2, t3}`. + @return An immediate awaitable containing `results{std::error_code(), t1, t2, t3}`. */ template -immediate> +immediate> ready(T1 t1, T2 t2, T3 t3) { return {{std::error_code(), std::move(t1), std::move(t2), std::move(t3)}}; } -/** Create an immediate awaitable for a failed io_result. +/** Create an immediate awaitable for a failed `results`. - This helper creates an @ref immediate wrapping an @ref io_result + This helper creates an @ref immediate wrapping an @ref results with an error code. @par Example @@ -177,57 +177,57 @@ ready(T1 t1, T2 t2, T3 t3) @param ec The error code. - @return An immediate awaitable containing a failed io_result. + @return An immediate awaitable containing a failed `results`. - @see immediate, io_result + @see immediate, results */ inline -immediate> +immediate> ready(std::error_code ec) noexcept { return {{ec}}; } -/** Create an immediate awaitable for an io_result with error and one value. +/** Create an immediate awaitable for a `results` with error and one value. @param ec The error code. @param t1 The result value. - @return An immediate awaitable containing `io_result{ec, t1}`. + @return An immediate awaitable containing `results{ec, t1}`. */ template -immediate> +immediate> ready(std::error_code ec, T1 t1) { return {{ec, std::move(t1)}}; } -/** Create an immediate awaitable for an io_result with error and two values. +/** Create an immediate awaitable for a `results` with error and two values. @param ec The error code. @param t1 The first result value. @param t2 The second result value. - @return An immediate awaitable containing `io_result{ec, t1, t2}`. + @return An immediate awaitable containing `results{ec, t1, t2}`. */ template -immediate> +immediate> ready(std::error_code ec, T1 t1, T2 t2) { return {{ec, std::move(t1), std::move(t2)}}; } -/** Create an immediate awaitable for an io_result with error and three values. +/** Create an immediate awaitable for a `results` with error and three values. @param ec The error code. @param t1 The first result value. @param t2 The second result value. @param t3 The third result value. - @return An immediate awaitable containing `io_result{ec, t1, t2, t3}`. + @return An immediate awaitable containing `results{ec, t1, t2, t3}`. */ template -immediate> +immediate> ready(std::error_code ec, T1 t1, T2 t2, T3 t3) { return {{ec, std::move(t1), std::move(t2), std::move(t3)}}; diff --git a/include/boost/capy/ex/run.hpp b/include/boost/capy/ex/run.hpp index 7b0d0eda1..70990b9d9 100644 --- a/include/boost/capy/ex/run.hpp +++ b/include/boost/capy/ex/run.hpp @@ -14,13 +14,13 @@ #include #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include @@ -190,7 +190,7 @@ inline boundary_trampoline make_boundary_trampoline() co_return; } -/** Awaitable that binds an IoRunnable to a specific executor. +/** Awaitable that binds a ContextRunnable to a specific executor. Stores the executor and inner task by value. When co_awaited, the co_await expression's lifetime extension keeps both alive for the @@ -200,22 +200,22 @@ inline boundary_trampoline make_boundary_trampoline() the inner task's `final_suspend` resumes the trampoline, which dispatches back through the caller's executor. - The `io_env` is owned by this awaitable and is guaranteed to + The `await_context` is owned by this awaitable and is guaranteed to outlive the inner task and all awaitables in its chain. Awaitables - may store `io_env const*` without concern for dangling references. + may store `await_context const*` without concern for dangling references. - @tparam Task The IoRunnable type + @tparam Task The ContextRunnable type @tparam Ex The executor type @tparam InheritStopToken If true, inherit caller's stop token @tparam Alloc The allocator type (void for no allocator) */ -template +template struct [[nodiscard]] run_awaitable_ex { Ex ex_; frame_memory_resource resource_; std::conditional_t st_; - io_env env_; + await_context env_; executor_ref caller_ex_; boundary_trampoline tr_; continuation task_cont_; @@ -293,7 +293,7 @@ struct [[nodiscard]] run_awaitable_ex return inner_.await_resume(); } - std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) { tr_ = make_boundary_trampoline(); auto& tp = tr_.h_.promise(); @@ -364,16 +364,16 @@ struct [[nodiscard]] run_awaitable_ex The inner task's `final_suspend` resumes the parent directly via unconditional symmetric transfer. - @tparam Task The IoRunnable type + @tparam Task The ContextRunnable type @tparam InheritStopToken If true, inherit caller's stop token @tparam Alloc The allocator type (void for no allocator) */ -template +template struct [[nodiscard]] run_awaitable { frame_memory_resource resource_; std::conditional_t st_; - io_env env_; + await_context env_; // Set while the parent is suspended awaiting the inner task. bool lent_ = false; Task inner_; // Last: destroyed first, while env_ is still valid @@ -434,7 +434,7 @@ struct [[nodiscard]] run_awaitable return inner_.await_resume(); } - std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* caller_env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* caller_env) { auto h = inner_.handle(); auto& p = h.promise(); @@ -519,7 +519,7 @@ class [[nodiscard]] run_wrapper_ex run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; - template + template [[nodiscard]] auto operator()(Task t) && { // The task's frame is made; the caller's own frames are not. @@ -579,7 +579,7 @@ class [[nodiscard]] run_wrapper_ex + template [[nodiscard]] auto operator()(Task t) && { // The task's frame is made; the caller's own frames are not. @@ -621,7 +621,7 @@ class [[nodiscard]] run_wrapper_ex run_wrapper_ex& operator=(run_wrapper_ex const&) = delete; run_wrapper_ex& operator=(run_wrapper_ex&&) = delete; - template + template [[nodiscard]] auto operator()(Task t) && { if constexpr (InheritStopToken) @@ -682,7 +682,7 @@ class [[nodiscard]] run_wrapper run_wrapper& operator=(run_wrapper const&) = delete; run_wrapper& operator=(run_wrapper&&) = delete; - template + template [[nodiscard]] auto operator()(Task t) && { // The task's frame is made; the caller's own frames are not. @@ -739,7 +739,7 @@ class [[nodiscard]] run_wrapper run_wrapper& operator=(run_wrapper const&) = delete; run_wrapper& operator=(run_wrapper&&) = delete; - template + template [[nodiscard]] auto operator()(Task t) && { // The task's frame is made; the caller's own frames are not. @@ -772,7 +772,7 @@ class [[nodiscard]] run_wrapper run_wrapper& operator=(run_wrapper const&) = delete; run_wrapper& operator=(run_wrapper&&) = delete; - template + template [[nodiscard]] auto operator()(Task t) && { return run_awaitable{std::move(t), std::move(st_)}; diff --git a/include/boost/capy/ex/run_async.hpp b/include/boost/capy/ex/run_async.hpp index 2b63f8b12..c152b9e51 100644 --- a/include/boost/capy/ex/run_async.hpp +++ b/include/boost/capy/ex/run_async.hpp @@ -15,11 +15,11 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include #include @@ -107,7 +107,7 @@ struct run_async_trampoline work_guard wg_; Handlers handlers_; frame_memory_resource resource_; - io_env env_; + await_context env_; invoke_fn invoke_ = nullptr; void* task_promise_ = nullptr; // task_h_: raw handle for frame_guard cleanup in make_trampoline. @@ -204,7 +204,7 @@ struct run_async_trampoline std::coroutine_handle h_; - template + template static void invoke_impl(void* p, Handlers& h) { using R = decltype(std::declval().await_resume()); @@ -232,7 +232,7 @@ struct run_async_trampoline work_guard wg_; Handlers handlers_; std::pmr::memory_resource* mr_; - io_env env_; + await_context env_; invoke_fn invoke_ = nullptr; void* task_promise_ = nullptr; // task_h_: raw handle for frame_guard cleanup in make_trampoline. @@ -309,7 +309,7 @@ struct run_async_trampoline std::coroutine_handle h_; - template + template static void invoke_impl(void* p, Handlers& h) { using R = decltype(std::declval().await_resume()); @@ -500,21 +500,21 @@ class [[nodiscard]] run_async_wrapper The rvalue ref-qualifier ensures the wrapper is consumed, enforcing correct LIFO destruction order. - The `io_env` constructed for the task is owned by the trampoline + The `await_context` constructed for the task is owned by the trampoline coroutine and is guaranteed to outlive the task and all awaitables - in its chain. Awaitables may store `io_env const*` without concern + in its chain. Awaitables may store `await_context const*` without concern for dangling references. @throws Any exception thrown by the executor's `dispatch`. The task is then destroyed without running, and the executor's work is released. - @tparam Task The IoRunnable type. + @tparam Task The ContextRunnable type. @param t The task to execute. Ownership is transferred to the run_async_trampoline which destroys it after completion. */ - template + template void operator()(Task t) && { auto task_h = t.handle(); diff --git a/include/boost/capy/ex/this_coro.hpp b/include/boost/capy/ex/this_coro.hpp index a21562302..4f7274fbe 100644 --- a/include/boost/capy/ex/this_coro.hpp +++ b/include/boost/capy/ex/this_coro.hpp @@ -27,7 +27,7 @@ namespace capy { @par !example example - @see io_awaitable_promise_base, io_env + @see context_awaitable_promise_base, await_context */ namespace this_coro { @@ -38,7 +38,7 @@ namespace this_coro { carries no data; it serves only as a sentinel for compile-time dispatch. @see environment - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ struct environment_tag {}; @@ -49,7 +49,7 @@ struct environment_tag {}; data; it serves only as a sentinel for compile-time dispatch. @see executor - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ struct executor_tag {}; @@ -60,7 +60,7 @@ struct executor_tag {}; no data; it serves only as a sentinel for compile-time dispatch. @see stop_token - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ struct stop_token_tag {}; @@ -71,7 +71,7 @@ struct stop_token_tag {}; no data; it serves only as a sentinel for compile-time dispatch. @see frame_allocator - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ struct frame_allocator_tag {}; @@ -79,7 +79,7 @@ struct frame_allocator_tag {}; Use `co_await this_coro::environment` inside a coroutine whose promise type supports environment access (e.g., inherits from - @ref io_awaitable_promise_base). The returned environment contains the + @ref context_awaitable_promise_base). The returned environment contains the executor, stop token, and allocator for this coroutine. @par Example @@ -87,18 +87,18 @@ struct frame_allocator_tag {}; @par Preconditions - An `io_env` must have been installed for this coroutine before the tag + An `await_context` must have been installed for this coroutine before the tag is awaited. Starting the coroutine via @ref run or `run_async` installs one; awaiting the tag without an installed environment is undefined behavior (an assertion fires in debug builds). @par Behavior - @li Returns a pointer to the stored `io_env` + @li Returns a pointer to the stored `await_context` @li This operation never suspends; `await_ready()` always returns `true` @see environment_tag - @see io_awaitable_promise_base - @see io_env + @see context_awaitable_promise_base + @see await_context */ inline constexpr environment_tag environment{}; @@ -106,7 +106,7 @@ inline constexpr environment_tag environment{}; Use `co_await this_coro::executor` inside a coroutine whose promise type supports executor access (e.g., inherits from - @ref io_awaitable_promise_base). The returned executor reflects the + @ref context_awaitable_promise_base). The returned executor reflects the executor this coroutine is bound to. @par Example @@ -114,19 +114,19 @@ inline constexpr environment_tag environment{}; @par Preconditions - An `io_env` must have been installed for this coroutine before the tag + An `await_context` must have been installed for this coroutine before the tag is awaited (see @ref environment). Awaiting it without an installed environment is undefined behavior (an assertion fires in debug builds). @par Behavior @li Returns the installed environment's `executor` field. If the started - chain installed an `io_env` whose `executor` was left default, the + chain installed an `await_context` whose `executor` was left default, the result is a default-constructed `executor_ref` (where `operator bool()` returns `false`). @li This operation never suspends; `await_ready()` always returns `true`. @see executor_tag - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ inline constexpr executor_tag executor{}; @@ -134,7 +134,7 @@ inline constexpr executor_tag executor{}; Use `co_await this_coro::stop_token` inside a coroutine whose promise type supports stop token access (e.g., inherits from - @ref io_awaitable_promise_base). The returned stop token reflects whatever + @ref context_awaitable_promise_base). The returned stop token reflects whatever token was passed to this coroutine when it was awaited. @par Example @@ -142,20 +142,20 @@ inline constexpr executor_tag executor{}; @par Preconditions - An `io_env` must have been installed for this coroutine before the tag + An `await_context` must have been installed for this coroutine before the tag is awaited (see @ref environment). Awaiting it without an installed environment is undefined behavior (an assertion fires in debug builds). @par Behavior @li Returns the installed environment's `stop_token` field. If the started - chain installed an `io_env` whose `stop_token` was left default, the + chain installed an `await_context` whose `stop_token` was left default, the result is a default-constructed `std::stop_token` (where `stop_possible()` returns `false`). @li The returned token remains valid for the coroutine's lifetime. @li This operation never suspends; `await_ready()` always returns `true`. @see stop_token_tag - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ inline constexpr stop_token_tag stop_token{}; @@ -163,7 +163,7 @@ inline constexpr stop_token_tag stop_token{}; Use `co_await this_coro::frame_allocator` inside a coroutine whose promise type supports frame allocator access (e.g., inherits from - @ref io_awaitable_promise_base). The returned pointer is the memory resource + @ref context_awaitable_promise_base). The returned pointer is the memory resource used for coroutine frame allocation. @par Example @@ -171,7 +171,7 @@ inline constexpr stop_token_tag stop_token{}; @par Preconditions - An `io_env` must have been installed for this coroutine before the tag + An `await_context` must have been installed for this coroutine before the tag is awaited (see @ref environment). Awaiting it without an installed environment is undefined behavior (an assertion fires in debug builds). @@ -181,7 +181,7 @@ inline constexpr stop_token_tag stop_token{}; @li This operation never suspends; `await_ready()` always returns `true`. @see frame_allocator_tag - @see io_awaitable_promise_base + @see context_awaitable_promise_base */ inline constexpr frame_allocator_tag frame_allocator{}; diff --git a/include/boost/capy/io/any_read_stream.hpp b/include/boost/capy/io/any_read_stream.hpp index cc0ee26f0..0f501f119 100644 --- a/include/boost/capy/io/any_read_stream.hpp +++ b/include/boost/capy/io/any_read_stream.hpp @@ -15,10 +15,10 @@ #include #include #include -#include +#include #include -#include -#include +#include +#include #include #include @@ -271,8 +271,8 @@ struct any_read_stream::vtable void* storage, std::span buffers); bool (*await_ready)(void*); - std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); - io_result (*await_resume)(void*); + std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, await_context const*); + results (*await_resume)(void*); void (*destroy_awaitable)(void*) noexcept; std::size_t awaitable_size; std::size_t awaitable_align; @@ -306,7 +306,7 @@ struct any_read_stream::vtable_for_impl +[](void* p) { return static_cast(p)->await_ready(); }, - +[](void* p, std::coroutine_handle<> h, io_env const* env) { + +[](void* p, std::coroutine_handle<> h, await_context const* env) { return detail::call_await_suspend( static_cast(p), h, env); }, @@ -467,13 +467,13 @@ any_read_stream::read_some(MB buffers) } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { return self_->vt_->await_suspend( self_->cached_awaitable_, h, env); } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { struct guard { diff --git a/include/boost/capy/io/any_write_stream.hpp b/include/boost/capy/io/any_write_stream.hpp index 170e0d8be..5a331d1ad 100644 --- a/include/boost/capy/io/any_write_stream.hpp +++ b/include/boost/capy/io/any_write_stream.hpp @@ -15,11 +15,11 @@ #include #include #include -#include +#include #include #include -#include -#include +#include +#include #include #include @@ -274,8 +274,8 @@ struct any_write_stream::vtable void* storage, std::span buffers); bool (*await_ready)(void*); - std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, io_env const*); - io_result (*await_resume)(void*); + std::coroutine_handle<> (*await_suspend)(void*, std::coroutine_handle<>, await_context const*); + results (*await_resume)(void*); void (*destroy_awaitable)(void*) noexcept; std::size_t awaitable_size; std::size_t awaitable_align; @@ -309,7 +309,7 @@ struct any_write_stream::vtable_for_impl +[](void* p) { return static_cast(p)->await_ready(); }, - +[](void* p, std::coroutine_handle<> h, io_env const* env) { + +[](void* p, std::coroutine_handle<> h, await_context const* env) { return detail::call_await_suspend( static_cast(p), h, env); }, @@ -470,13 +470,13 @@ any_write_stream::write_some(CB buffers) } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { return self_->vt_->await_suspend( self_->cached_awaitable_, h, env); } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { if(!owns_) diff --git a/include/boost/capy/io/write_now.hpp b/include/boost/capy/io/write_now.hpp index 2372ea2c8..e9009e150 100644 --- a/include/boost/capy/io/write_now.hpp +++ b/include/boost/capy/io/write_now.hpp @@ -14,14 +14,14 @@ #include #include #include -#include +#include #include #include #include #include #include -#include -#include +#include +#include #include #include @@ -98,10 +98,10 @@ class write_now { struct promise_type { - io_result result_; + results result_; std::exception_ptr ep_; std::coroutine_handle<> cont_{nullptr}; - io_env const* env_ = nullptr; + await_context const* env_ = nullptr; bool done_ = false; // The awaiting op_type while a parent awaits this frame. @@ -148,7 +148,7 @@ class write_now } void return_value( - io_result r) noexcept + results r) noexcept { result_ = r; } @@ -167,7 +167,7 @@ class write_now auto await_transform(A&& a) { using decayed = std::decay_t; - if constexpr (IoAwaitable) + if constexpr (ContextAwaitable) { struct wrapper { @@ -264,7 +264,7 @@ class write_now std::coroutine_handle<> await_suspend( std::coroutine_handle<> cont, - io_env const* env) + await_context const* env) { auto& p = h_.promise(); p.cont_ = cont; @@ -273,7 +273,7 @@ class write_now return h_; } - [[nodiscard]] io_result await_resume() + [[nodiscard]] results await_resume() { auto& p = h_.promise(); p.owner_ = nullptr; @@ -369,10 +369,10 @@ class write_now cb.consume(std::get<1>(r)); total_written += std::get<1>(r); if(std::get<0>(r)) - co_return io_result{ + co_return results{ std::get<0>(r), total_written}; } - co_return io_result{ + co_return results{ std::error_code(), total_written}; } #else @@ -394,14 +394,14 @@ class write_now break; auto r = inner.await_resume(); if(std::get<0>(r)) - co_return io_result{ + co_return results{ std::get<0>(r), total_written}; cb.consume(std::get<1>(r)); total_written += std::get<1>(r); } if(total_written >= total_size) - co_return io_result{ + co_return results{ std::error_code(), total_written}; co_yield 0; @@ -413,10 +413,10 @@ class write_now cb.consume(std::get<1>(r)); total_written += std::get<1>(r); if(std::get<0>(r)) - co_return io_result{ + co_return results{ std::get<0>(r), total_written}; } - co_return io_result{ + co_return results{ std::error_code(), total_written}; } #endif diff --git a/include/boost/capy/io_task.hpp b/include/boost/capy/io_task.hpp deleted file mode 100644 index d26d150b5..000000000 --- a/include/boost/capy/io_task.hpp +++ /dev/null @@ -1,38 +0,0 @@ -// -// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com) -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/capy -// - -#ifndef BOOST_CAPY_IO_TASK_HPP -#define BOOST_CAPY_IO_TASK_HPP - -#include -#include -#include - -namespace boost { -namespace capy { - -/** Names `task>`, whose `co_return` can convert an error code directly. - - This is a convenience alias for `task>`. - The tuple converting constructor allows direct `co_return` - of error codes: - - @par !example example - - - @tparam Ts Additional value types beyond error_code. -*/ -template -using io_task = task>; - -} // namespace capy -} // namespace boost - -#endif diff --git a/include/boost/capy/quitter.hpp b/include/boost/capy/quitter.hpp index f89f6f1d1..8db10d878 100644 --- a/include/boost/capy/quitter.hpp +++ b/include/boost/capy/quitter.hpp @@ -13,9 +13,9 @@ #include #include #include -#include -#include -#include +#include +#include +#include #include #include @@ -122,11 +122,11 @@ struct quitter_return_base quitter must not be awaited twice. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @tparam T The result type. Use `quitter<>` for `quitter`. - @see task, IoRunnable, IoAwaitable + @see task, ContextRunnable, ContextAwaitable */ template struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE @@ -137,7 +137,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE This is the promise object the compiler associates with a `quitter` coroutine. It satisfies the coroutine promise requirements and participates in the I/O awaitable protocol via - @ref io_awaitable_promise_base. Unlike @ref task::promise_type, + @ref context_awaitable_promise_base. Unlike @ref task::promise_type, its `transform_awaitable` checks the stop token before each awaited result reaches the body. A pending stop request throws an internal sentinel exception that unwinds to a "stopped" @@ -147,10 +147,10 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE Result storage and `return_value`/`return_void` are provided by `detail::quitter_return_base`. - @see io_awaitable_promise_base, IoRunnable + @see context_awaitable_promise_base, ContextRunnable */ struct promise_type - : io_awaitable_promise_base + : context_awaitable_promise_base , detail::quitter_return_base { private: @@ -325,7 +325,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE // transform_awaitable — the key difference from task //------------------------------------------------------ - /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable. + /** Awaiter wrapping a nested `co_await` of an @ref ContextAwaitable. Forwards the environment to the inner awaitable's environment-taking `await_suspend` and restores the @@ -395,7 +395,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE This is the plain `await_suspend` the compiler calls for the nested `co_await`. It forwards to the wrapped awaitable's - @ref IoAwaitable overload, supplying the promise's stored + @ref ContextAwaitable overload, supplying the promise's stored environment as the second argument. It then hands back that call's result unchanged, so the wrapped awaitable's suspension decision, whatever form it takes, is preserved. @@ -431,10 +431,10 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE /** Transform a nested awaitable before `co_await`. - Wraps an @ref IoAwaitable in a @ref transform_awaiter so the + Wraps an @ref ContextAwaitable in a @ref transform_awaiter so the coroutine's environment is propagated into it and the stop token is checked on resumption. A diagnostic is emitted if the - awaitable does not satisfy @ref IoAwaitable. + awaitable does not satisfy @ref ContextAwaitable. @param a The awaitable expression from `co_await a`. @@ -444,7 +444,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE auto transform_awaitable(Awaitable&& a) { using A = std::decay_t; - if constexpr (IoAwaitable) + if constexpr (ContextAwaitable) { return transform_awaiter{ std::forward(a), this}; @@ -452,7 +452,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE else { static_assert(sizeof(A) == 0, - "requires IoAwaitable"); + "requires ContextAwaitable"); } } }; @@ -535,7 +535,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE */ std::coroutine_handle<> await_suspend( std::coroutine_handle<> cont, - io_env const* env) + await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(env); diff --git a/include/boost/capy/read.hpp b/include/boost/capy/read.hpp index 97a30d396..91249d775 100644 --- a/include/boost/capy/read.hpp +++ b/include/boost/capy/read.hpp @@ -13,7 +13,8 @@ #include #include -#include +#include +#include #include #include #include @@ -38,7 +39,7 @@ namespace capy { is performed. This is not a contingency. @par Await-returns - An object of type `io_result` destructuring as `[ec, n]`. + An object of type `results` destructuring as `[ec, n]`. Upon a contingency, `n` represents the number of bytes read so far, inclusive of the last partial read. @@ -65,11 +66,11 @@ namespace capy { @param buffers The buffer sequence to fill. If the lifetime of the buffer sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined. - @return A task yielding `io_result` whose second element + @return A task yielding `results` whose second element is the number of bytes read. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Example @@ -83,7 +84,7 @@ template requires ReadStream && MutableBufferSequence auto read(S& stream, MB buffers) -> - io_task + task> { consuming_buffers consuming(buffers); std::size_t const total_size = buffer_size(buffers); diff --git a/include/boost/capy/read_at_least.hpp b/include/boost/capy/read_at_least.hpp index 7e9e7a89b..754a5b76e 100644 --- a/include/boost/capy/read_at_least.hpp +++ b/include/boost/capy/read_at_least.hpp @@ -12,7 +12,8 @@ #include #include -#include +#include +#include #include #include #include @@ -53,7 +54,7 @@ namespace capy { not a contingency. @par Await-returns - An object of type `io_result` destructuring as `[ec, n]`. + An object of type `results` destructuring as `[ec, n]`. Upon a contingency, the count represents the number of bytes read so far, inclusive of the last partial read. @@ -86,11 +87,11 @@ namespace capy { @param n The minimum number of bytes to read. Must not exceed `buffer_size(buffers)`. - @return A task yielding `io_result` whose second element + @return A task yielding `results` whose second element is the number of bytes read. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Example @@ -103,7 +104,7 @@ template requires ReadStream && MutableBufferSequence auto read_at_least(S& stream, MB buffers, std::size_t n) -> - io_task + task> { consuming_buffers consuming(buffers); std::size_t const total_size = buffer_size(buffers); diff --git a/include/boost/capy/io_result.hpp b/include/boost/capy/results.hpp similarity index 82% rename from include/boost/capy/io_result.hpp rename to include/boost/capy/results.hpp index f434756a6..68f53c698 100644 --- a/include/boost/capy/io_result.hpp +++ b/include/boost/capy/results.hpp @@ -8,8 +8,8 @@ // Official repository: https://github.com/cppalliance/capy // -#ifndef BOOST_CAPY_IO_RESULT_HPP -#define BOOST_CAPY_IO_RESULT_HPP +#ifndef BOOST_CAPY_RESULTS_HPP +#define BOOST_CAPY_RESULTS_HPP #include #include @@ -49,26 +49,26 @@ namespace capy { `std::error_code`. */ template -using io_result = std::tuple; +using results = std::tuple; namespace detail { // Outcomes are structural, not nominal: any std::tuple whose first -// element is error_code is an io_result, regardless of how the +// element is error_code is a `results`, regardless of how the // producing operation spelled it. template -struct is_io_result : std::false_type {}; +struct is_results : std::false_type {}; template -struct is_io_result> +struct is_results> : std::true_type {}; template -inline constexpr bool is_io_result_v = is_io_result::value; +inline constexpr bool is_results_v = is_results::value; } // namespace detail } // namespace capy } // namespace boost -#endif // BOOST_CAPY_IO_RESULT_HPP +#endif // BOOST_CAPY_RESULTS_HPP diff --git a/include/boost/capy/task.hpp b/include/boost/capy/task.hpp index aabf6c792..89d3e8fe9 100644 --- a/include/boost/capy/task.hpp +++ b/include/boost/capy/task.hpp @@ -13,12 +13,12 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include -#include +#include #include #include @@ -111,27 +111,27 @@ struct task_return_base @tparam T The result type. Use `task<>` for `task`. - @see IoRunnable, IoAwaitable, run, run_async + @see ContextRunnable, ContextAwaitable, run, run_async */ template struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE task { - /** Stores `task`'s result and joins the I/O awaitable protocol via `io_awaitable_promise_base`. + /** Stores `task`'s result and joins the I/O awaitable protocol via `context_awaitable_promise_base`. This is the promise object the compiler associates with a `task` coroutine. It satisfies the coroutine promise requirements and participates in the I/O awaitable protocol via - @ref io_awaitable_promise_base. It is part of the coroutine + @ref context_awaitable_promise_base. It is part of the coroutine machinery and is not intended to be used directly by callers. Result storage and `return_value`/`return_void` are provided by `detail::task_return_base`. - @see io_awaitable_promise_base, IoRunnable + @see context_awaitable_promise_base, ContextRunnable */ struct promise_type - : io_awaitable_promise_base + : context_awaitable_promise_base , detail::task_return_base { private: @@ -253,7 +253,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE has_ep_ = true; } - /** Awaiter wrapping a nested `co_await` of an @ref IoAwaitable. + /** Awaiter wrapping a nested `co_await` of an @ref ContextAwaitable. Forwards the environment to the inner awaitable's environment-taking `await_suspend` and restores the @@ -303,7 +303,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE This is the plain `await_suspend` the compiler calls for the nested `co_await`. It forwards to the wrapped awaitable's - @ref IoAwaitable overload, supplying the promise's stored + @ref ContextAwaitable overload, supplying the promise's stored environment as the second argument. It then hands back that call's result unchanged, so the wrapped awaitable's suspension decision, whatever form it takes, is preserved. @@ -332,9 +332,9 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE /** Transform a nested awaitable before `co_await`. - Wraps an @ref IoAwaitable in a @ref transform_awaiter so the + Wraps an @ref ContextAwaitable in a @ref transform_awaiter so the coroutine's environment is propagated into it. A diagnostic - is emitted if the awaitable does not satisfy @ref IoAwaitable. + is emitted if the awaitable does not satisfy @ref ContextAwaitable. @param a The awaitable expression from `co_await a`. @@ -344,14 +344,14 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE auto transform_awaitable(Awaitable&& a) { using A = std::decay_t; - if constexpr (IoAwaitable) + if constexpr (ContextAwaitable) { return transform_awaiter{ std::forward(a), this}; } else { - static_assert(IoAwaitable, "requires IoAwaitable"); + static_assert(ContextAwaitable, "requires ContextAwaitable"); } } }; @@ -400,11 +400,11 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE @throws The exception captured by the coroutine body, if any. - @note Discarding an `io_result` silently drops the error + @note Discarding a `results` silently drops the error code, so that overload is marked `[[nodiscard]]`. */ [[nodiscard]] auto await_resume() - requires detail::is_io_result_v + requires detail::is_results_v { lent_ = false; if(h_.promise().has_ep_) @@ -413,7 +413,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE } auto await_resume() - requires (! detail::is_io_result_v) + requires (! detail::is_results_v) { lent_ = false; if(h_.promise().has_ep_) @@ -439,7 +439,7 @@ struct [[nodiscard]] BOOST_CAPY_CORO_AWAIT_ELIDABLE @return The task's coroutine handle, for symmetric transfer. */ - std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(env); diff --git a/include/boost/capy/test/bufgrind.hpp b/include/boost/capy/test/bufgrind.hpp index 88a08c3dc..7f013ba4d 100644 --- a/include/boost/capy/test/bufgrind.hpp +++ b/include/boost/capy/test/bufgrind.hpp @@ -15,7 +15,7 @@ #include #include #include -#include +#include #include #include @@ -121,7 +121,7 @@ class bufgrind @return @p h, so the caller resumes immediately. */ - std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const*) const noexcept { return h; } + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const*) const noexcept { return h; } /** Return the current split and advance to the next. diff --git a/include/boost/capy/test/fuse.hpp b/include/boost/capy/test/fuse.hpp index c50c0ff73..6554c776c 100644 --- a/include/boost/capy/test/fuse.hpp +++ b/include/boost/capy/test/fuse.hpp @@ -12,7 +12,7 @@ #define BOOST_CAPY_TEST_FUSE_HPP #include -#include +#include #include #include #include @@ -515,7 +515,7 @@ class fuse encountering a failure. First runs the complete loop using error codes, then runs using exceptions. - This overload handles lambdas that return an @ref IoRunnable + This overload handles lambdas that return an @ref ContextRunnable (such as `task`), executing them synchronously via @ref run_blocking. @@ -533,7 +533,7 @@ class fuse of the last @ref maybe_fail or @ref fail call. */ template - requires IoRunnable> + requires ContextRunnable> result armed(F&& fn) { @@ -542,7 +542,7 @@ class fuse /** Run a coroutine test function on a caller-supplied runner. - Behaves like the @ref IoRunnable overload of @ref armed, but + Behaves like the @ref ContextRunnable overload of @ref armed, but instead of driving each iteration through @ref run_blocking, it hands the coroutine to `run_one`. This lets a caller run each iteration on any execution context it chooses. Operations built @@ -552,7 +552,7 @@ class fuse the caller owns the drive loop. @par Runner contract - `run_one` is invoked once per round with the @ref IoRunnable + `run_one` is invoked once per round with the @ref ContextRunnable produced by `fn`. It must run that task to completion synchronously and *return* any exception the task raised as a `std::exception_ptr` (null on success). It must not rethrow. @@ -575,7 +575,7 @@ class fuse @return A @ref result indicating success or failure. */ template - requires IoRunnable> + requires ContextRunnable> && std::same_as< std::invoke_result_t>, std::exception_ptr> @@ -620,7 +620,7 @@ class fuse @see armed */ template - requires IoRunnable> + requires ContextRunnable> result operator()(F&& fn) { @@ -698,7 +698,7 @@ class fuse of the @ref fail call. */ template - requires IoRunnable> + requires ContextRunnable> result inert(F&& fn) { diff --git a/include/boost/capy/test/read_stream.hpp b/include/boost/capy/test/read_stream.hpp index c077f5d26..1c9ac7e44 100644 --- a/include/boost/capy/test/read_stream.hpp +++ b/include/boost/capy/test/read_stream.hpp @@ -17,8 +17,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -148,21 +148,21 @@ class read_stream bool await_ready() const noexcept { return false; } // The operation completes synchronously, but await_suspend - // is the only place io_env is delivered (the promise's + // is the only place await_context is delivered (the promise's // transform_awaiter forwards it here). Returning false means // the coroutine does not actually suspend — it resumes // immediately — so the read still completes synchronously - // while having observed the stop token. See io_env, IoAwaitable. + // while having observed the stop token. See await_context, ContextAwaitable. bool await_suspend( std::coroutine_handle<>, - io_env const* env) noexcept + await_context const* env) noexcept { canceled_ = env->stop_token.stop_requested(); return false; } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { // Empty buffer is a no-op regardless of diff --git a/include/boost/capy/test/run_blocking.hpp b/include/boost/capy/test/run_blocking.hpp index 9571dc554..50b1916e3 100644 --- a/include/boost/capy/test/run_blocking.hpp +++ b/include/boost/capy/test/run_blocking.hpp @@ -422,11 +422,11 @@ class [[nodiscard]] run_blocking_wrapper @throws std::system_error with `error::canceled` if the task is abandoned before it completes. - @tparam Task The IoRunnable type. + @tparam Task The ContextRunnable type. @param t The task to execute. */ - template + template void operator()(Task t) && { diff --git a/include/boost/capy/test/stream.hpp b/include/boost/capy/test/stream.hpp index 069b3997e..6c1f25d1e 100644 --- a/include/boost/capy/test/stream.hpp +++ b/include/boost/capy/test/stream.hpp @@ -17,8 +17,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -338,7 +338,7 @@ class stream bool await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { // A callback registered after stop was requested would // fire inline and post this coroutine, which could then be @@ -365,7 +365,7 @@ class stream return true; } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { if(stop_cb_active_) @@ -461,13 +461,13 @@ class stream bool await_suspend( std::coroutine_handle<>, - io_env const* env) noexcept + await_context const* env) noexcept { canceled_ = env->stop_token.stop_requested(); return false; } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { std::size_t n = buffer_size(buffers_); diff --git a/include/boost/capy/test/write_stream.hpp b/include/boost/capy/test/write_stream.hpp index 00211fcf0..d26781d3a 100644 --- a/include/boost/capy/test/write_stream.hpp +++ b/include/boost/capy/test/write_stream.hpp @@ -16,8 +16,8 @@ #include #include #include -#include -#include +#include +#include #include #include @@ -170,21 +170,21 @@ class write_stream bool await_ready() const noexcept { return false; } // The operation completes synchronously, but await_suspend is - // the only place io_env is delivered (the promise's + // the only place await_context is delivered (the promise's // transform_awaiter forwards it here). Returning false means // the coroutine does not actually suspend; it resumes - // immediately, having observed the stop token. See io_env, - // IoAwaitable. + // immediately, having observed the stop token. See await_context, + // ContextAwaitable. bool await_suspend( std::coroutine_handle<>, - io_env const* env) noexcept + await_context const* env) noexcept { canceled_ = env->stop_token.stop_requested(); return false; } - [[nodiscard]] io_result + [[nodiscard]] results await_resume() { if(buffer_empty(buffers_)) diff --git a/include/boost/capy/when_all.hpp b/include/boost/capy/when_all.hpp index 9f44fbf6e..95edf980e 100644 --- a/include/boost/capy/when_all.hpp +++ b/include/boost/capy/when_all.hpp @@ -12,16 +12,16 @@ #define BOOST_CAPY_WHEN_ALL_HPP #include -#include +#include #include #include -#include +#include #include #include #include #include #include -#include +#include #include #include @@ -97,7 +97,7 @@ struct when_all_core std::optional parent_stop_callback_; continuation continuation_; - io_env const* caller_env_ = nullptr; + await_context const* caller_env_ = nullptr; // Set when a branch is abandoned before finishing: the parent is // then abandoned rather than resumed. @@ -163,10 +163,10 @@ struct when_all_state /** Shared state for homogeneous when_all (range overload). - Stores extracted io_result payloads in a vector indexed by task + Stores extracted results payloads in a vector indexed by task position. Tracks the first error_code for error propagation. - @tparam T The payload type extracted from io_result. + @tparam T The payload type extracted from `results`. */ template struct when_all_homogeneous_state @@ -200,7 +200,7 @@ struct when_all_homogeneous_state } }; -/** Specialization for void io_result children (no payload storage). */ +/** Specialization for void `results` children (no payload storage). */ template<> struct when_all_homogeneous_state> { @@ -242,7 +242,7 @@ struct when_all_runner { StateType* state_ = nullptr; std::size_t index_ = 0; - io_env env_; + await_context env_; // Owns the branch's segment. chain_owner segment_; bool done_ = false; @@ -337,14 +337,14 @@ struct when_all_runner auto await_transform(Awaitable&& a) { using A = std::decay_t; - if constexpr (IoAwaitable) + if constexpr (ContextAwaitable) { return transform_awaiter{ std::forward(a), this}; } else { - static_assert(sizeof(A) == 0, "requires IoAwaitable"); + static_assert(sizeof(A) == 0, "requires ContextAwaitable"); } } }; @@ -372,12 +372,12 @@ struct when_all_runner } }; -/** Create an io_result-aware runner for a single awaitable (range path). +/** Create a `results`-aware runner for a single awaitable (range path). Checks the error code, records errors and requests stop on failure, or extracts the payload on success. */ -template +template when_all_runner make_when_all_homogeneous_runner(Awaitable* inner, StateType* state, std::size_t index) { @@ -397,20 +397,20 @@ make_when_all_homogeneous_runner(Awaitable* inner, StateType* state, std::size_t } else { - using PayloadT = io_result_payload_t< + using PayloadT = results_payload_t< awaitable_result_t>; if constexpr (!std::is_same_v>) { state->set_result(index, - extract_io_payload(std::move(result))); + extract_results_payload(std::move(result))); } } } -/** Create a runner for io_result children that requests stop on ec. */ -template +/** Create a runner for `results` children that requests stop on ec. */ +template when_all_runner> -make_when_all_io_runner(Awaitable* inner, when_all_state* state) +make_when_all_results_runner(Awaitable* inner, when_all_state* state) { // Taken from the join's storage only once running, into a local: // a runner destroyed unstarted owns no branch, and one destroyed @@ -429,9 +429,9 @@ make_when_all_io_runner(Awaitable* inner, when_all_state* state) } } -/** Launcher that uses io_result-aware runners. */ -template -class when_all_io_launcher +/** Launcher that uses `results`-aware runners. */ +template +class when_all_results_launcher { using state_type = when_all_state...>; @@ -439,7 +439,7 @@ class when_all_io_launcher state_type* state_; public: - when_all_io_launcher( + when_all_results_launcher( std::tuple* awaitables, state_type* state) : awaitables_(awaitables) @@ -453,7 +453,7 @@ class when_all_io_launcher } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> continuation, io_env const* caller_env) + std::coroutine_handle<> continuation, await_context const* caller_env) { state_->core_.continuation_.h = continuation; state_->core_.continuation_.owner = caller_env->owner; @@ -489,13 +489,13 @@ class when_all_io_launcher template void launch_one(executor_ref caller_ex, std::stop_token token) { - auto runner = make_when_all_io_runner( + auto runner = make_when_all_results_runner( &std::get(*awaitables_), state_); auto h = runner.release(); h.promise().state_ = state_; h.promise().segment_.bind(h, caller_ex); - h.promise().env_ = io_env{caller_ex, token, + h.promise().env_ = await_context{caller_ex, token, state_->core_.caller_env_->frame_allocator, &h.promise().segment_}; @@ -521,7 +521,7 @@ auto extract_results(when_all_state& state) { return [&](std::index_sequence) { // Explicit element types: CTAD would collapse a single - // io_result child via the tuple copy deduction guide + // `results` child via the tuple copy deduction guide return std::tuple< decltype(extract_single_result(state))...>( extract_single_result(state)...); @@ -537,7 +537,7 @@ template class when_all_homogeneous_launcher { using Awaitable = std::ranges::range_value_t; - using PayloadT = io_result_payload_t>; + using PayloadT = results_payload_t>; Range* range_; when_all_homogeneous_state* state_; @@ -556,7 +556,7 @@ class when_all_homogeneous_launcher return std::ranges::empty(*range_); } - std::coroutine_handle<> await_suspend(std::coroutine_handle<> continuation, io_env const* caller_env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> continuation, await_context const* caller_env) { state_->core_.continuation_.h = continuation; state_->core_.continuation_.owner = caller_env->owner; @@ -585,7 +585,7 @@ class when_all_homogeneous_launcher h.promise().state_ = state_; h.promise().index_ = index; h.promise().segment_.bind(h, caller_env->executor); - h.promise().env_ = io_env{caller_env->executor, token, + h.promise().env_ = await_context{caller_env->executor, token, caller_env->frame_allocator, &h.promise().segment_}; state_->runner_handles_[index].h = std::coroutine_handle<>{h}; @@ -609,12 +609,12 @@ class when_all_homogeneous_launcher } // namespace detail -/** Execute a range of io_result-returning awaitables concurrently. +/** Execute a range of `results`-returning awaitables concurrently. Starts all awaitables simultaneously and waits for all to complete. On success, extracted payloads are collected in a vector preserving input order. The first error_code makes a stop request that every - sibling observes, and is propagated in the outer io_result. + sibling observes, and is propagated in the outer results. Exceptions always beat error codes. @li All child awaitables run concurrently on the caller's executor. @@ -645,9 +645,9 @@ class when_all_homogeneous_launcher completes only after every child has finished. @par Await-returns - An object of type `io_result>` destructuring as + An object of type `results>` destructuring as `[ec, values]`, where `PayloadT` is the payload of one child's - `io_result`. + `results`. `ec` is the first non-zero `ec` await-returned by a child, in completion order rather than input order. The `ec` of every other @@ -667,18 +667,18 @@ class when_all_homogeneous_launcher payload per input awaitable; otherwise `values` is empty. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Thread Safety The returned task must be awaited from a single execution context. Child awaitables execute concurrently but complete through the caller's executor. - @param awaitables Range of io_result-returning awaitables to execute + @param awaitables Range of `results`-returning awaitables to execute concurrently (must not be empty). - @return A task yielding io_result> where PayloadT - is the payload extracted from each child's io_result. + @return A task yielding `results>` where PayloadT + is the payload extracted from each child's results. @throws std::invalid_argument if range is empty (thrown before coroutine suspends). @@ -697,22 +697,22 @@ class when_all_homogeneous_launcher @par !example example_1 - @see IoAwaitableRange, when_all + @see ContextAwaitableRange, when_all */ -template - requires detail::is_io_result_v< +template + requires detail::is_results_v< awaitable_result_t>> && (!std::is_same_v< - detail::io_result_payload_t< + detail::results_payload_t< awaitable_result_t>>, std::tuple<>>) [[nodiscard]] auto when_all(R&& awaitables) - -> task task>>>>> { using Awaitable = std::ranges::range_value_t; - using PayloadT = detail::io_result_payload_t< + using PayloadT = detail::results_payload_t< awaitable_result_t>; using OwnedRange = detail::owned_range_t; @@ -731,20 +731,20 @@ template std::rethrow_exception(state.core_.first_exception_); if(state.has_error_.load(std::memory_order_relaxed)) - co_return io_result>{state.first_error_, {}}; + co_return results>{state.first_error_, {}}; - std::vector results; - results.reserve(count); + std::vector values; + values.reserve(count); for(auto& opt : state.results_) - results.push_back(std::move(*opt)); + values.push_back(std::move(*opt)); - co_return io_result>{std::error_code(), std::move(results)}; + co_return results>{std::error_code(), std::move(values)}; } -/** Execute a range of void io_result-returning awaitables concurrently. +/** Execute a range of void `results`-returning awaitables concurrently. Starts all awaitables simultaneously and waits for all to complete. - Since all awaitables return io_result<>, no payload values are + Since all awaitables return results<>, no payload values are collected. The first error_code makes a stop request that every sibling observes, and is propagated. Exceptions always beat error codes. @@ -771,7 +771,7 @@ template completes only after every child has finished. @par Await-returns - An object of type `io_result<>` destructuring as `[ec]`. The children + An object of type `results<>` destructuring as `[ec]`. The children have no payloads, so nothing else is reported. `ec` is the first non-zero `ec` await-returned by a child, in @@ -787,17 +787,17 @@ template await-returned success. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Thread Safety The returned task must be awaited from a single execution context. Child awaitables execute concurrently but complete through the caller's executor. - @param awaitables Range of io_result<>-returning awaitables to + @param awaitables Range of `results<>`-returning awaitables to execute concurrently (must not be empty). - @return A task yielding io_result<> whose ec is the first child + @return A task yielding `results<>` whose ec is the first child error, or default-constructed on success. @throws std::invalid_argument if range is empty. @@ -816,16 +816,16 @@ template @par !example example_2 - @see IoAwaitableRange, when_all + @see ContextAwaitableRange, when_all */ -template - requires detail::is_io_result_v< +template + requires detail::is_results_v< awaitable_result_t>> && std::is_same_v< - detail::io_result_payload_t< + detail::results_payload_t< awaitable_result_t>>, std::tuple<>> -[[nodiscard]] auto when_all(R&& awaitables) -> task> +[[nodiscard]] auto when_all(R&& awaitables) -> task> { using OwnedRange = detail::owned_range_t; @@ -844,16 +844,16 @@ template std::rethrow_exception(state.core_.first_exception_); if(state.has_error_.load(std::memory_order_relaxed)) - co_return io_result<>{state.first_error_}; + co_return results<>{state.first_error_}; - co_return io_result<>{}; + co_return results<>{}; } -/** Execute io_result-returning awaitables concurrently, inspecting error codes. +/** Execute `results`-returning awaitables concurrently, inspecting error codes. - Overload selected when all children return io_result. + Overload selected when all children return results. The error_code is lifted out of each child into a single outer - io_result. On success all values are returned; on failure the + results. On success all values are returned; on failure the first error_code wins. @par Await-effects @@ -875,9 +875,9 @@ template completes only after every child has finished. @par Await-returns - An object of type `io_result` destructuring as + An object of type `results` destructuring as `[ec, v1, ..., vn]`, where `Pi` is the payload of the i-th child's - `io_result`. + `results`. `ec` is the first non-zero `ec` await-returned by a child, in completion order rather than argument order. The `ec` of every other @@ -898,7 +898,7 @@ template and `ec` is success only if every child await-returned success. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Thread Safety The returned task must be awaited from a single execution context. @@ -916,31 +916,31 @@ template has finished or been abandoned. @param awaitables One or more awaitables each returning - io_result. + results. - @return A task yielding io_result where each Ri + @return A task yielding `results` where each Ri follows the payload flattening rules. */ -template +template requires (sizeof...(As) > 0) - && detail::all_io_result_awaitables + && detail::all_results_awaitables [[nodiscard]] auto when_all(As... awaitables) - -> task>...>> + -> task>...>> { - using result_type = io_result< - detail::io_result_payload_t>...>; + using result_type = results< + detail::results_payload_t>...>; detail::when_all_state...> state; std::tuple awaitable_tuple(std::move(awaitables)...); - co_await detail::when_all_io_launcher(&awaitable_tuple, &state); + co_await detail::when_all_results_launcher(&awaitable_tuple, &state); // Exception always wins over error_code if(state.core_.first_exception_) std::rethrow_exception(state.core_.first_exception_); - auto r = detail::build_when_all_io_result( + auto r = detail::build_when_all_results( detail::extract_results(state)); if(state.has_error_.load(std::memory_order_relaxed)) std::get<0>(r) = state.first_error_; diff --git a/include/boost/capy/when_any.hpp b/include/boost/capy/when_any.hpp index 9518aa653..66833b131 100644 --- a/include/boost/capy/when_any.hpp +++ b/include/boost/capy/when_any.hpp @@ -12,10 +12,10 @@ #define BOOST_CAPY_WHEN_ANY_HPP #include -#include +#include #include #include -#include +#include #include #include #include @@ -23,7 +23,7 @@ #include #include #include -#include +#include #include #include @@ -42,12 +42,12 @@ #include /* - when_any - Race multiple io_result tasks, select first success + when_any - Race multiple results tasks, select first success ============================================================= OVERVIEW: --------- - when_any launches N io_result-returning tasks concurrently. A task + when_any launches N results-returning tasks concurrently. A task wins by returning !ec; errors and exceptions do not win. Once a winner is found, stop is requested for siblings and the winner's payload is returned. If no winner exists (all fail), one of the @@ -64,8 +64,8 @@ Key components: - when_any_core: Shared state tracking winner and completion - - when_any_io_runner: Wrapper coroutine for each child task - - when_any_io_launcher/when_any_io_homogeneous_launcher: + - when_any_results_runner: Wrapper coroutine for each child task + - when_any_results_launcher/when_any_results_homogeneous_launcher: Awaitables that start all runners concurrently CRITICAL INVARIANTS: @@ -155,7 +155,7 @@ struct when_any_core std::optional parent_stop_callback_; continuation continuation_; - io_env const* caller_env_ = nullptr; + await_context const* caller_env_ = nullptr; // Set when a branch is abandoned before finishing: the parent is // then abandoned rather than resumed. @@ -208,9 +208,9 @@ struct when_any_core namespace detail { -// State for io_result-aware when_any: only !ec wins. +// State for `results`-aware when_any: only !ec wins. template -struct when_any_io_state +struct when_any_results_state { static constexpr std::size_t task_count = sizeof...(Ts); using variant_type = std::variant; @@ -226,7 +226,7 @@ struct when_any_io_state std::error_code last_error_; std::exception_ptr last_exception_; - when_any_io_state() + when_any_results_state() : core_(task_count) { } @@ -246,17 +246,17 @@ struct when_any_io_state } }; -// Wrapper coroutine for io_result-aware when_any children. +// Wrapper coroutine for `results`-aware when_any children. // unhandled_exception records the exception but does NOT claim winner status. template -struct when_any_io_runner +struct when_any_results_runner { struct promise_type : detail::process_frame_mixin { StateType* state_ = nullptr; std::size_t index_ = 0; - io_env env_; + await_context env_; // Owns the branch's segment. chain_owner segment_; bool done_ = false; @@ -267,9 +267,9 @@ struct when_any_io_runner state_->core_.lose_branch(); } - when_any_io_runner get_return_object() noexcept + when_any_results_runner get_return_object() noexcept { - return when_any_io_runner( + return when_any_results_runner( std::coroutine_handle::from_promise(*this)); } @@ -318,7 +318,7 @@ struct when_any_io_runner void return_void() noexcept {} - // Exceptions do NOT win in io_result when_any + // Exceptions do NOT win in `results` when_any void unhandled_exception() noexcept { state_->record_exception(std::current_exception()); @@ -348,33 +348,33 @@ struct when_any_io_runner auto await_transform(Awaitable&& a) { using A = std::decay_t; - if constexpr (IoAwaitable) + if constexpr (ContextAwaitable) { return transform_awaiter{ std::forward(a), this}; } else { - static_assert(sizeof(A) == 0, "requires IoAwaitable"); + static_assert(sizeof(A) == 0, "requires ContextAwaitable"); } } }; std::coroutine_handle h_; - explicit when_any_io_runner(std::coroutine_handle h) noexcept + explicit when_any_results_runner(std::coroutine_handle h) noexcept : h_(h) { } - when_any_io_runner(when_any_io_runner&& other) noexcept + when_any_results_runner(when_any_results_runner&& other) noexcept : h_(std::exchange(other.h_, nullptr)) { } - when_any_io_runner(when_any_io_runner const&) = delete; - when_any_io_runner& operator=(when_any_io_runner const&) = delete; - when_any_io_runner& operator=(when_any_io_runner&&) = delete; + when_any_results_runner(when_any_results_runner const&) = delete; + when_any_results_runner& operator=(when_any_results_runner const&) = delete; + when_any_results_runner& operator=(when_any_results_runner&&) = delete; auto release() noexcept { @@ -383,9 +383,9 @@ struct when_any_io_runner }; // Runner coroutine: only tries to win when the child returns !ec. -template -when_any_io_runner -make_when_any_io_runner(Awaitable* inner, StateType* state) +template +when_any_results_runner +make_when_any_results_runner(Awaitable* inner, StateType* state) { // Taken from the join's storage only once running, into a local: // a runner destroyed unstarted owns no branch, and one destroyed @@ -405,7 +405,7 @@ make_when_any_io_runner(Awaitable* inner, StateType* state) { state->result_.emplace( std::in_place_index, - detail::extract_io_payload(std::move(result))); + detail::extract_results_payload(std::move(result))); } catch(...) { @@ -420,18 +420,18 @@ make_when_any_io_runner(Awaitable* inner, StateType* state) } } -// Launcher for io_result-aware when_any. -template -class when_any_io_launcher +// Launcher for `results`-aware when_any. +template +class when_any_results_launcher { - using state_type = when_any_io_state< - io_result_payload_t>...>; + using state_type = when_any_results_state< + results_payload_t>...>; std::tuple* tasks_; state_type* state_; public: - when_any_io_launcher( + when_any_results_launcher( std::tuple* tasks, state_type* state) : tasks_(tasks) @@ -445,7 +445,7 @@ class when_any_io_launcher } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> continuation, io_env const* caller_env) + std::coroutine_handle<> continuation, await_context const* caller_env) { state_->core_.continuation_.h = continuation; state_->core_.continuation_.owner = caller_env->owner; @@ -481,14 +481,14 @@ class when_any_io_launcher template void launch_one(executor_ref caller_ex, std::stop_token token) { - auto runner = make_when_any_io_runner( + auto runner = make_when_any_results_runner( &std::get(*tasks_), state_); auto h = runner.release(); h.promise().state_ = state_; h.promise().index_ = I; h.promise().segment_.bind(h, caller_ex); - h.promise().env_ = io_env{caller_ex, token, + h.promise().env_ = await_context{caller_ex, token, state_->core_.caller_env_->frame_allocator, &h.promise().segment_}; @@ -497,12 +497,12 @@ class when_any_io_launcher } }; -/** Shared state for homogeneous io_result-aware when_any (range overload). +/** Shared state for homogeneous `results`-aware when_any (range overload). - @tparam T The payload type extracted from io_result. + @tparam T The payload type extracted from `results`. */ template -struct when_any_io_homogeneous_state +struct when_any_results_homogeneous_state { when_any_core core_; std::optional result_; @@ -512,7 +512,7 @@ struct when_any_io_homogeneous_state std::error_code last_error_; std::exception_ptr last_exception_; - explicit when_any_io_homogeneous_state(std::size_t count) + explicit when_any_results_homogeneous_state(std::size_t count) : core_(count) , runner_handles_(std::make_unique(count)) { @@ -533,9 +533,9 @@ struct when_any_io_homogeneous_state } }; -/** Specialization for void io_result children (no payload storage). */ +/** Specialization for void `results` children (no payload storage). */ template<> -struct when_any_io_homogeneous_state> +struct when_any_results_homogeneous_state> { when_any_core core_; std::unique_ptr runner_handles_; @@ -544,7 +544,7 @@ struct when_any_io_homogeneous_state> std::error_code last_error_; std::exception_ptr last_exception_; - explicit when_any_io_homogeneous_state(std::size_t count) + explicit when_any_results_homogeneous_state(std::size_t count) : core_(count) , runner_handles_(std::make_unique(count)) { @@ -565,13 +565,13 @@ struct when_any_io_homogeneous_state> } }; -/** Create an io_result-aware runner for homogeneous when_any (range path). +/** Create a `results`-aware runner for homogeneous when_any (range path). Only tries to win when the child returns !ec. */ -template -when_any_io_runner -make_when_any_io_homogeneous_runner( +template +when_any_results_runner +make_when_any_results_homogeneous_runner( Awaitable* inner, StateType* state, std::size_t index) { // Taken from the join's storage only once running, into a local: @@ -587,14 +587,14 @@ make_when_any_io_homogeneous_runner( { if(state->core_.try_win(index)) { - using PayloadT = io_result_payload_t< + using PayloadT = results_payload_t< awaitable_result_t>; if constexpr (!std::is_same_v>) { try { state->result_.emplace( - extract_io_payload(std::move(result))); + extract_results_payload(std::move(result))); } catch(...) { @@ -610,20 +610,20 @@ make_when_any_io_homogeneous_runner( } } -/** Starts all io_result-aware homogeneous runners concurrently. */ -template -class when_any_io_homogeneous_launcher +/** Starts all `results`-aware homogeneous runners concurrently. */ +template +class when_any_results_homogeneous_launcher { using Awaitable = std::ranges::range_value_t; - using PayloadT = io_result_payload_t>; + using PayloadT = results_payload_t>; Range* range_; - when_any_io_homogeneous_state* state_; + when_any_results_homogeneous_state* state_; public: - when_any_io_homogeneous_launcher( + when_any_results_homogeneous_launcher( Range* range, - when_any_io_homogeneous_state* state) + when_any_results_homogeneous_state* state) : range_(range) , state_(state) { @@ -635,7 +635,7 @@ class when_any_io_homogeneous_launcher } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> continuation, io_env const* caller_env) + std::coroutine_handle<> continuation, await_context const* caller_env) { state_->core_.continuation_.h = continuation; state_->core_.continuation_.owner = caller_env->owner; @@ -657,14 +657,14 @@ class when_any_io_homogeneous_launcher std::size_t index = 0; for(auto&& a : *range_) { - auto runner = make_when_any_io_homogeneous_runner( + auto runner = make_when_any_results_homogeneous_runner( &a, state_, index); auto h = runner.release(); h.promise().state_ = state_; h.promise().index_ = index; h.promise().segment_.bind(h, caller_env->executor); - h.promise().env_ = io_env{caller_env->executor, token, + h.promise().env_ = await_context{caller_env->executor, token, caller_env->frame_allocator, &h.promise().segment_}; state_->runner_handles_[index].h = std::coroutine_handle<>{h}; @@ -685,7 +685,7 @@ class when_any_io_homogeneous_launcher } // namespace detail -/** Race a range of io_result-returning awaitables (non-void payloads). +/** Race a range of `results`-returning awaitables (non-void payloads). Only a child returning !ec can win. Errors and exceptions do not claim winner status. If all children fail, an unspecified one of @@ -717,7 +717,7 @@ class when_any_io_homogeneous_launcher @par Await-returns An object of type `std::variant>`, - where `PayloadT` is the payload of one child's `io_result`. + where `PayloadT` is the payload of one child's `results`. @li Index 1 holds the winner's position in the input range paired with its payload. @@ -739,7 +739,7 @@ class when_any_io_homogeneous_launcher or a failed child's exception is rethrown. @par Remarks - Supports _IoAwaitable cancellation_. A canceled child await-returns a + Supports _ContextAwaitable cancellation_. A canceled child await-returns a non-zero `ec` and so cannot win; if no child has already succeeded, the result settles at index 0. @@ -748,7 +748,7 @@ class when_any_io_homogeneous_launcher Child awaitables execute concurrently but complete through the caller's executor. - @param awaitables Range of io_result-returning awaitables (must + @param awaitables Range of `results`-returning awaitables (must not be empty). @return A task yielding variant> @@ -774,23 +774,23 @@ class when_any_io_homogeneous_launcher @par !example example_1 - @see IoAwaitableRange, when_any + @see ContextAwaitableRange, when_any */ -template - requires detail::is_io_result_v< +template + requires detail::is_results_v< awaitable_result_t>> && (!std::is_same_v< - detail::io_result_payload_t< + detail::results_payload_t< awaitable_result_t>>, std::tuple<>>) [[nodiscard]] auto when_any(R&& awaitables) -> task>>>>> { using Awaitable = std::ranges::range_value_t; - using PayloadT = detail::io_result_payload_t< + using PayloadT = detail::results_payload_t< awaitable_result_t>; using result_type = std::variant>; @@ -802,9 +802,9 @@ template OwnedRange owned_awaitables = detail::own_range(std::forward(awaitables)); - detail::when_any_io_homogeneous_state state(count); + detail::when_any_results_homogeneous_state state(count); - co_await detail::when_any_io_homogeneous_launcher( + co_await detail::when_any_results_homogeneous_launcher( &owned_awaitables, &state); // Winner found @@ -822,7 +822,7 @@ template co_return result_type{std::in_place_index<0>, state.last_error_}; } -/** Race a range of void io_result-returning awaitables. +/** Race a range of void `results`-returning awaitables. Only a child returning !ec can win. Returns the winner's index at variant index 1, or error_code at index 0 on all-fail. @@ -868,7 +868,7 @@ template or a failed child's exception is rethrown. @par Remarks - Supports _IoAwaitable cancellation_. A canceled child await-returns a + Supports _ContextAwaitable cancellation_. A canceled child await-returns a non-zero `ec` and so cannot win; if no child has already succeeded, the result settles at index 0. @@ -877,7 +877,7 @@ template Child awaitables execute concurrently but complete through the caller's executor. - @param awaitables Range of io_result<>-returning awaitables (must + @param awaitables Range of `results<>`-returning awaitables (must not be empty). @return A task yielding variant where index 0 @@ -899,13 +899,13 @@ template @par !example example_2 - @see IoAwaitableRange, when_any + @see ContextAwaitableRange, when_any */ -template - requires detail::is_io_result_v< +template + requires detail::is_results_v< awaitable_result_t>> && std::is_same_v< - detail::io_result_payload_t< + detail::results_payload_t< awaitable_result_t>>, std::tuple<>> [[nodiscard]] auto when_any(R&& awaitables) @@ -920,9 +920,9 @@ template OwnedRange owned_awaitables = detail::own_range(std::forward(awaitables)); - detail::when_any_io_homogeneous_state> state(count); + detail::when_any_results_homogeneous_state> state(count); - co_await detail::when_any_io_homogeneous_launcher( + co_await detail::when_any_results_homogeneous_launcher( &owned_awaitables, &state); // Winner found @@ -940,9 +940,9 @@ template co_return result_type{std::in_place_index<0>, state.last_error_}; } -/** Race io_result-returning awaitables, selecting the first success. +/** Race `results`-returning awaitables, selecting the first success. - Overload selected when all children return io_result. + Overload selected when all children return results. Only a child returning !ec can win. Errors and exceptions do not claim winner status. @@ -967,7 +967,7 @@ template @par Await-returns An object of type `std::variant`, where - `Pi` is the payload of the i-th child's `io_result`. + `Pi` is the payload of the i-th child's `results`. @li Index i+1 identifies the i-th argument as the winner and holds its payload. @@ -989,7 +989,7 @@ template result holds index 0, or a failed child's exception is rethrown. @par Remarks - Supports _IoAwaitable cancellation_. A canceled child await-returns a + Supports _ContextAwaitable cancellation_. A canceled child await-returns a non-zero `ec` and so cannot win; if no child has already succeeded, the result settles at index 0. @@ -999,7 +999,7 @@ template executor. @param as The awaitables to race. Each must satisfy @ref - IoAwaitable and is consumed (moved-from) when `when_any` + ContextAwaitable and is consumed (moved-from) when `when_any` is awaited. @return A task yielding variant where @@ -1027,23 +1027,23 @@ template parent is abandoned rather than resumed once the last branch has finished or been abandoned. */ -template +template requires (sizeof...(As) > 0) - && detail::all_io_result_awaitables + && detail::all_results_awaitables [[nodiscard]] auto when_any(As... as) -> task>...>> + detail::results_payload_t>...>> { using result_type = std::variant< std::error_code, - detail::io_result_payload_t>...>; + detail::results_payload_t>...>; - detail::when_any_io_state< - detail::io_result_payload_t>...> state; + detail::when_any_results_state< + detail::results_payload_t>...> state; std::tuple awaitable_tuple(std::move(as)...); - co_await detail::when_any_io_launcher( + co_await detail::when_any_results_launcher( &awaitable_tuple, &state); // Winner found: return their result diff --git a/include/boost/capy/write.hpp b/include/boost/capy/write.hpp index 8ec858065..a894efb17 100644 --- a/include/boost/capy/write.hpp +++ b/include/boost/capy/write.hpp @@ -12,7 +12,8 @@ #define BOOST_CAPY_WRITE_HPP #include -#include +#include +#include #include #include #include @@ -40,7 +41,7 @@ namespace capy { @par Await-returns - An object of type `io_result` destructuring as `[ec, n]`. + An object of type `results` destructuring as `[ec, n]`. Upon a contingency, `n` represents the number of bytes written so far. @@ -73,22 +74,22 @@ namespace capy { sequence represented by `buffers` ends before the coroutine finishes, the behavior is undefined. - @return A task yielding `io_result` whose second element + @return A task yielding `results` whose second element is the number of bytes written. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Example @par !example example - @see WriteStream, ConstBufferSequence, IoAwaitable, io_result, cond. + @see WriteStream, ConstBufferSequence, ContextAwaitable, results, cond. */ template -auto write(S& stream, CB buffers) -> io_task +auto write(S& stream, CB buffers) -> task> { consuming_buffers consuming(buffers); std::size_t const total_size = buffer_size(buffers); diff --git a/include/boost/capy/write_at_least.hpp b/include/boost/capy/write_at_least.hpp index b0879ab28..fbebd62cb 100644 --- a/include/boost/capy/write_at_least.hpp +++ b/include/boost/capy/write_at_least.hpp @@ -11,7 +11,8 @@ #define BOOST_CAPY_WRITE_AT_LEAST_HPP #include -#include +#include +#include #include #include #include @@ -51,7 +52,7 @@ namespace capy { not a contingency. @par Await-returns - An object of type `io_result` destructuring as `[ec, n]`. + An object of type `results` destructuring as `[ec, n]`. Upon a contingency, the count represents the number of bytes written so far. @@ -84,11 +85,11 @@ namespace capy { @param n The minimum number of bytes to write. Must not exceed `buffer_size(buffers)`. - @return A task yielding `io_result` whose second element + @return A task yielding `results` whose second element is the number of bytes written. @par Remarks - Supports _IoAwaitable cancellation_. + Supports _ContextAwaitable cancellation_. @par Example @@ -99,7 +100,7 @@ namespace capy { */ template auto -write_at_least(S& stream, CB buffers, std::size_t n) -> io_task +write_at_least(S& stream, CB buffers, std::size_t n) -> task> { consuming_buffers consuming(buffers); std::size_t const total_size = buffer_size(buffers); diff --git a/index.html b/index.html deleted file mode 100644 index 36bc0acad..000000000 --- a/index.html +++ /dev/null @@ -1,21 +0,0 @@ - - - Boost.Buffers - - - - Automatic redirection failed, please go to - ./doc/html/index.html -


- - Boost.Buffers
-
- Copyright (C) 2023 Vinnie Falco
-
- Distributed under the Boost Software License, Version 1.0. - (See accompanying file LICENSE_1_0.txt or copy at - http://www.boost.org/LICENSE_1_0.txt)
-
-
- - diff --git a/test/doc/programs/4d_executors_executor_ref.cpp b/test/doc/programs/4d_executors_executor_ref.cpp index fcb5138e3..637a1b6ad 100644 --- a/test/doc/programs/4d_executors_executor_ref.cpp +++ b/test/doc/programs/4d_executors_executor_ref.cpp @@ -11,8 +11,8 @@ // Full program shown in pages/4.coroutines/4c.executors.adoc. #include +#include #include -#include #include #include #include @@ -34,7 +34,7 @@ struct park bool await_ready() const noexcept { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const*) + std::coroutine_handle<> h, capy::await_context const*) { parked.h = h; parked_ready.release(); diff --git a/test/doc/programs/8m_parallel_tasks_variadic.cpp b/test/doc/programs/8m_parallel_tasks_variadic.cpp index 2380ac1c0..855638067 100644 --- a/test/doc/programs/8m_parallel_tasks_variadic.cpp +++ b/test/doc/programs/8m_parallel_tasks_variadic.cpp @@ -20,7 +20,7 @@ namespace capy = boost::capy; // Sum integers in [lo, hi) -capy::io_task partial_sum(int lo, int hi) +capy::task> partial_sum(int lo, int hi) { // tag::thread_id[] std::ostringstream oss; @@ -32,7 +32,7 @@ capy::io_task partial_sum(int lo, int hi) long long sum = 0; for (int i = lo; i < hi; ++i) sum += i; - co_return capy::io_result{std::error_code(), sum}; + co_return capy::results{std::error_code(), sum}; } int main() diff --git a/test/doc/reference/IoAwaitable.concept.cpp b/test/doc/reference/ContextAwaitable.concept.cpp similarity index 82% rename from test/doc/reference/IoAwaitable.concept.cpp rename to test/doc/reference/ContextAwaitable.concept.cpp index 142c51831..9124253e7 100644 --- a/test/doc/reference/IoAwaitable.concept.cpp +++ b/test/doc/reference/ContextAwaitable.concept.cpp @@ -7,9 +7,9 @@ // Official repository: https://github.com/cppalliance/capy // -// Reference examples for boost::capy::IoAwaitable, injected into its documentation by +// Reference examples for boost::capy::ContextAwaitable, injected into its documentation by // doc/addons/extensions/reference-snippets.lua. Declared in: -// include/boost/capy/concept/io_awaitable.hpp +// include/boost/capy/concept/context_awaitable.hpp // // The tagged regions are what the reference renders; the includes, // suppressions and namespaces around them are scaffolding. Each region gets @@ -42,7 +42,7 @@ void start_my_io_op(std::stop_token, Completion&&) {} // tag::example[] class my_awaitable { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_; std::error_code ec_ {}; @@ -50,7 +50,7 @@ class my_awaitable bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, capy::io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, capy::await_context const* env) noexcept { env_ = env; // store the pointer, never a copy cont_ = capy::continuation{h}; @@ -66,11 +66,11 @@ class my_awaitable return std::noop_coroutine(); // go back to scheduler } - capy::io_result<> await_resume() const noexcept { return {ec_}; } + capy::results<> await_resume() const noexcept { return {ec_}; } }; // end::example[] -static_assert( capy::IoAwaitable ); +static_assert( capy::ContextAwaitable ); } // namespace ex_1 } // namespace diff --git a/test/doc/reference/IoRunnable.concept.cpp b/test/doc/reference/ContextRunnable.concept.cpp similarity index 84% rename from test/doc/reference/IoRunnable.concept.cpp rename to test/doc/reference/ContextRunnable.concept.cpp index f7272bd69..a298c4f69 100644 --- a/test/doc/reference/IoRunnable.concept.cpp +++ b/test/doc/reference/ContextRunnable.concept.cpp @@ -7,9 +7,9 @@ // Official repository: https://github.com/cppalliance/capy // -// Reference examples for boost::capy::IoRunnable, injected into its documentation by +// Reference examples for boost::capy::ContextRunnable, injected into its documentation by // doc/addons/extensions/reference-snippets.lua. Declared in: -// include/boost/capy/concept/io_runnable.hpp +// include/boost/capy/concept/context_runnable.hpp // // The tagged regions are what the reference renders; the includes, // suppressions and namespaces around them are scaffolding. Each region gets @@ -40,7 +40,7 @@ class T std::exception_ptr exception() noexcept; int result(); // non-void tasks only void set_continuation(std::coroutine_handle<>) noexcept; - void set_environment(capy::io_env const*) noexcept; + void set_environment(capy::await_context const*) noexcept; }; std::coroutine_handle handle() const noexcept; diff --git a/test/doc/reference/ReadStream.concept.cpp b/test/doc/reference/ReadStream.concept.cpp index adf6c3f24..2aad1d312 100644 --- a/test/doc/reference/ReadStream.concept.cpp +++ b/test/doc/reference/ReadStream.concept.cpp @@ -33,7 +33,7 @@ namespace { namespace ex_1 { // tag::example_1[] template< capy::MutableBufferSequence MB > -capy::IoAwaitable auto read_some( MB buffers ); +capy::ContextAwaitable auto read_some( MB buffers ); // end::example_1[] } // namespace ex_1 diff --git a/test/doc/reference/WriteStream.concept.cpp b/test/doc/reference/WriteStream.concept.cpp index f9a03b943..1fb4f07c7 100644 --- a/test/doc/reference/WriteStream.concept.cpp +++ b/test/doc/reference/WriteStream.concept.cpp @@ -33,7 +33,7 @@ namespace { namespace ex_1 { // tag::example_1[] template< capy::ConstBufferSequence Buffers > -capy::IoAwaitable auto write_some( Buffers buffers ); +capy::ContextAwaitable auto write_some( Buffers buffers ); // end::example_1[] } // namespace ex_1 diff --git a/test/doc/reference/consuming_buffers.record.cpp b/test/doc/reference/consuming_buffers.record.cpp index eec42eef3..3165318f5 100644 --- a/test/doc/reference/consuming_buffers.record.cpp +++ b/test/doc/reference/consuming_buffers.record.cpp @@ -32,7 +32,7 @@ namespace { namespace ex_1 { // tag::example[] template -capy::io_task read_until_full( capy::any_read_stream& stream, Buffers buffers ) +capy::task> read_until_full( capy::any_read_stream& stream, Buffers buffers ) { capy::consuming_buffers consuming(buffers); std::size_t total = 0, want = capy::buffer_size(buffers); diff --git a/test/doc/reference/io_awaitable_promise_base.record.cpp b/test/doc/reference/context_awaitable_promise_base.record.cpp similarity index 90% rename from test/doc/reference/io_awaitable_promise_base.record.cpp rename to test/doc/reference/context_awaitable_promise_base.record.cpp index a1da50f03..3c3029b2d 100644 --- a/test/doc/reference/io_awaitable_promise_base.record.cpp +++ b/test/doc/reference/context_awaitable_promise_base.record.cpp @@ -7,9 +7,9 @@ // Official repository: https://github.com/cppalliance/capy // -// Reference examples for boost::capy::io_awaitable_promise_base, injected into its documentation by +// Reference examples for boost::capy::context_awaitable_promise_base, injected into its documentation by // doc/addons/extensions/reference-snippets.lua. Declared in: -// include/boost/capy/ex/io_awaitable_promise_base.hpp +// include/boost/capy/ex/context_awaitable_promise_base.hpp // // The tagged regions are what the reference renders; the includes, // suppressions and namespaces around them are scaffolding. Each region gets @@ -37,7 +37,7 @@ namespace ex_1 { // variations on promise plumbing. struct my_task { - struct promise_type : capy::io_awaitable_promise_base + struct promise_type : capy::context_awaitable_promise_base { my_task get_return_object() { @@ -91,7 +91,7 @@ my_task example() namespace ex_2 { // tag::example_2[] -struct promise_type : capy::io_awaitable_promise_base +struct promise_type : capy::context_awaitable_promise_base { template auto transform_awaitable(A&& a) @@ -109,7 +109,7 @@ namespace ex_3 { // section's point is the await_suspend overload below, not the promise. struct my_task { - struct promise_type : capy::io_awaitable_promise_base + struct promise_type : capy::context_awaitable_promise_base { my_task get_return_object() { @@ -147,9 +147,9 @@ struct my_task std::coroutine_handle h_; - // IoAwaitable await_suspend receives and stores the environment, + // ContextAwaitable await_suspend receives and stores the environment, // then resumes into the coroutine body via symmetric transfer - std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, capy::io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, capy::await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(env); diff --git a/test/doc/reference/immediate.record.cpp b/test/doc/reference/immediate.record.cpp index b18a0245e..85b9acde8 100644 --- a/test/doc/reference/immediate.record.cpp +++ b/test/doc/reference/immediate.record.cpp @@ -50,14 +50,14 @@ namespace ex_2 { struct my_sync_sink { template - capy::immediate> + capy::immediate> write(CB buffers) { auto n = process_sync(buffers); return {{std::error_code(), n}}; } - capy::immediate> + capy::immediate> write_eof() { return {{}}; diff --git a/test/doc/reference/io_task.typedef.cpp b/test/doc/reference/io_task.typedef.cpp deleted file mode 100644 index 063118558..000000000 --- a/test/doc/reference/io_task.typedef.cpp +++ /dev/null @@ -1,49 +0,0 @@ -// -// Copyright (c) 2026 Michael Vandeberg -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -// Official repository: https://github.com/cppalliance/capy -// - -// Reference examples for boost::capy::io_task, injected into its documentation by -// doc/addons/extensions/reference-snippets.lua. Declared in: -// include/boost/capy/io_task.hpp -// -// The tagged regions are what the reference renders; the includes, -// suppressions and namespaces around them are scaffolding. Each region gets -// its own namespace so that examples which reuse a name still compile. - -#include "../doc_warnings.hpp" - -#include - -#include -#include -#include -#include -#include -#include - -namespace capy = boost::capy; - -namespace { - -namespace ex_1 { -// tag::example[] -capy::io_task read_one( capy::any_read_stream& s, capy::mutable_buffer buf ) -{ - co_return co_await s.read_some(buf); // returns io_result -} - -capy::io_task<> require_ready(bool ready) -{ - if(!ready) - co_return capy::make_error_code(capy::error::eof); // error_code converts to io_result<> - co_return {}; -} -// end::example[] -} // namespace ex_1 - -} // namespace diff --git a/test/doc/reference/ready.function.cpp b/test/doc/reference/ready.function.cpp index bcaf9122b..2d96d3890 100644 --- a/test/doc/reference/ready.function.cpp +++ b/test/doc/reference/ready.function.cpp @@ -41,14 +41,14 @@ std::error_code connect_sync() return {}; } -capy::immediate> +capy::immediate> write(capy::const_buffer buf) { auto n = write_all_sync(buf); return capy::ready(n); // success with n bytes } -capy::immediate> +capy::immediate> connect() { connect_sync(); @@ -65,7 +65,7 @@ std::error_code write_checked_sync(capy::const_buffer buf) return {}; } -capy::immediate> +capy::immediate> write(capy::const_buffer buf) { auto ec = write_checked_sync(buf); diff --git a/test/doc/reference/io_result.typedef.cpp b/test/doc/reference/results.typedef.cpp similarity index 91% rename from test/doc/reference/io_result.typedef.cpp rename to test/doc/reference/results.typedef.cpp index 106abe1e1..021e1924e 100644 --- a/test/doc/reference/io_result.typedef.cpp +++ b/test/doc/reference/results.typedef.cpp @@ -7,9 +7,9 @@ // Official repository: https://github.com/cppalliance/capy // -// Reference examples for boost::capy::io_result, injected into its documentation by +// Reference examples for boost::capy::results, injected into its documentation by // doc/addons/extensions/reference-snippets.lua. Declared in: -// include/boost/capy/io_result.hpp +// include/boost/capy/results.hpp // // The tagged regions are what the reference renders; the includes, // suppressions and namespaces around them are scaffolding. Each region gets diff --git a/test/doc/reference/when_all.function.cpp b/test/doc/reference/when_all.function.cpp index 7f02e3f1b..35d8f4068 100644 --- a/test/doc/reference/when_all.function.cpp +++ b/test/doc/reference/when_all.function.cpp @@ -31,7 +31,7 @@ namespace capy = boost::capy; namespace { namespace ex_1 { // tag::example_1[] -capy::io_task read_one( capy::any_read_stream& stream, capy::mutable_buffer buf ) +capy::task> read_one( capy::any_read_stream& stream, capy::mutable_buffer buf ) { co_return co_await stream.read_some(buf); } @@ -41,7 +41,7 @@ capy::task read_all_connections( { // One awaitable per stream: each stream is touched by exactly one // child, so running them concurrently is safe. - std::vector> reads; + std::vector>> reads; for (std::size_t i = 0; i < streams.size(); ++i) reads.push_back(read_one(streams[i], buffers[i])); @@ -58,9 +58,9 @@ namespace ex_2 { template< class MakeJob > capy::task run_n_jobs( MakeJob make_job, int n ) { - std::vector> jobs; + std::vector>> jobs; for (int i = 0; i < n; ++i) - jobs.push_back(make_job(i)); // io_task<> per index + jobs.push_back(make_job(i)); // task> per index auto [ec] = co_await capy::when_all(std::move(jobs)); } diff --git a/test/doc/reference/when_any.function.cpp b/test/doc/reference/when_any.function.cpp index 06b69268c..92c2f6f1f 100644 --- a/test/doc/reference/when_any.function.cpp +++ b/test/doc/reference/when_any.function.cpp @@ -31,7 +31,7 @@ namespace capy = boost::capy; namespace { namespace ex_1 { // tag::example_1[] -capy::io_task read_from( capy::any_read_stream& stream, capy::mutable_buffer buf ) +capy::task> read_from( capy::any_read_stream& stream, capy::mutable_buffer buf ) { co_return co_await stream.read_some(buf); } @@ -41,7 +41,7 @@ capy::task read_first_ready( { // One awaitable per stream: each stream is touched by exactly one // child, so racing them concurrently is safe. - std::vector> reads; + std::vector>> reads; for (std::size_t i = 0; i < streams.size(); ++i) reads.push_back(read_from(streams[i], buffers[i])); @@ -55,19 +55,19 @@ capy::task read_first_ready( } // namespace ex_1 namespace ex_2 { // tag::example_2[] -capy::io_task<> background_work_a() +capy::task> background_work_a() { co_return {}; } -capy::io_task<> background_work_b() +capy::task> background_work_b() { co_return {}; } capy::task example() { - std::vector> jobs; + std::vector>> jobs; jobs.push_back(background_work_a()); jobs.push_back(background_work_b()); diff --git a/test/doc/snippets/4b_tasks.cpp b/test/doc/snippets/4b_tasks.cpp index 13172d256..d51ebe0fa 100644 --- a/test/doc/snippets/4b_tasks.cpp +++ b/test/doc/snippets/4b_tasks.cpp @@ -77,22 +77,22 @@ capy::task<> log_message(std::string msg) } // namespace returning -namespace io_results { +namespace reporting_errors { -// tag::io_task[] -// io_result holds an error code `ec` plus zero or more payload -// values. io_task is just an alias for task>. +// tag::results[] +// `results` holds an error code `ec` plus zero or more payload +// values. A task returning one reports failure without throwing. -capy::io_task<> ensure_ready(bool ready) +capy::task> ensure_ready(bool ready) { if(! ready) co_return std::make_error_code(std::errc::not_connected); // ec converts co_return {}; // success } -capy::io_task count_ready(bool ready) +capy::task> count_ready(bool ready) { - using result = capy::io_result; + using result = capy::results; if(! ready) co_return result{std::make_error_code(std::errc::not_connected), 0}; co_return result{std::error_code(), 42}; // success, carrying a value @@ -100,15 +100,15 @@ capy::io_task count_ready(bool ready) capy::task<> use_them() { - // io_result models the tuple protocol: ec first, then the payloads. + // `results` models the tuple protocol: ec first, then the payloads. auto [ec, n] = co_await count_ready(true); if(ec) co_return; // always check ec first (void)n; // n is only meaningful when ec is falsy } -// end::io_task[] +// end::results[] -} // namespace io_results +} // namespace reporting_errors namespace awaiting { diff --git a/test/doc/snippets/4e_io_awaitable.cpp b/test/doc/snippets/4e_context_awaitable.cpp similarity index 82% rename from test/doc/snippets/4e_io_awaitable.cpp rename to test/doc/snippets/4e_context_awaitable.cpp index 3b0517630..07c1fe6c7 100644 --- a/test/doc/snippets/4e_io_awaitable.cpp +++ b/test/doc/snippets/4e_context_awaitable.cpp @@ -8,13 +8,13 @@ // Official repository: https://github.com/cppalliance/capy // -// Compiled fragments shown in pages/4.coroutines/4d.io-awaitable.adoc. +// Compiled fragments shown in pages/4.coroutines/4d.context-awaitable.adoc. #include "../doc_warnings.hpp" -#include +#include #include -#include +#include #include #include #include @@ -59,21 +59,21 @@ struct std_awaiter_handle // end::std_await_suspend[] }; -struct io_awaiter_signature +struct context_awaiter_signature { // tag::two_arg_await_suspend[] - auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env); + auto await_suspend(std::coroutine_handle<> h, capy::await_context const* env); // end::two_arg_await_suspend[] }; -static_assert(capy::IoAwaitable>); -static_assert(!capy::IoAwaitable); +static_assert(capy::ContextAwaitable>); +static_assert(!capy::ContextAwaitable); struct caller_promise { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; - capy::io_env const* environment() const noexcept { return env_; } + capy::await_context const* environment() const noexcept { return env_; } }; template @@ -95,7 +95,7 @@ struct transform_awaiter_sketch struct context_flow_child { std::coroutine_handle<> await_suspend( - std::coroutine_handle<>, capy::io_env const*) + std::coroutine_handle<>, capy::await_context const*) { return std::noop_coroutine(); } @@ -119,7 +119,7 @@ void store_result() {} // tag::my_awaitable[] struct my_awaitable { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_; result_type result_; @@ -129,7 +129,7 @@ struct my_awaitable } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { // Store pointer to environment, never copy env_ = env; @@ -161,7 +161,7 @@ struct my_awaitable // end::my_awaitable[] // tag::runnable_awaitable[] -// A complete IoAwaitable following the pattern above. It produces a +// A complete ContextAwaitable following the pattern above. It produces a // value, then resumes the caller on the caller's own executor by posting // its continuation. A real one would do this from an async completion // callback; here the "operation" finishes immediately. @@ -169,7 +169,7 @@ struct add_awaitable { int a_; int b_; - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; // Defaulted, so the call site supplies only a_ and b_. capy::continuation cont_{}; result_type result_{}; @@ -177,7 +177,7 @@ struct add_awaitable bool await_ready() const noexcept { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { env_ = env; cont_.h = h; @@ -189,7 +189,7 @@ struct add_awaitable result_type await_resume() { return result_; } }; -// A task awaits the custom IoAwaitable and returns what it delivered. +// A task awaits the custom ContextAwaitable and returns what it delivered. capy::task add_via_awaitable() { co_return co_await add_awaitable{2, 3}; @@ -205,7 +205,7 @@ struct stoppable_awaitable bool await_ready() { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { if (env->stop_token.stop_requested()) return h; // Already cancelled, resume immediately @@ -230,7 +230,7 @@ struct wrong_stop_callback_demo { std::optional>> stop_cb_; - void emplace_wrong(std::coroutine_handle<> h, capy::io_env const* env) + void emplace_wrong(std::coroutine_handle<> h, capy::await_context const* env) { // tag::wrong_stop_callback[] // WRONG: resumes coroutine on the calling thread @@ -240,18 +240,18 @@ struct wrong_stop_callback_demo }; // Mirrors the shape of task.hpp's transform_awaitable; the shown -// static_assert sits in the branch taken for non-IoAwaitables. +// static_assert sits in the branch taken for non-ContextAwaitables. template void await_transform_sketch() { - if constexpr (capy::IoAwaitable) + if constexpr (capy::ContextAwaitable) { } else { // tag::reject_plain[] - // In task.hpp, when the awaited type is not an IoAwaitable: - static_assert(sizeof(A) == 0, "requires IoAwaitable"); + // In task.hpp, when the awaited type is not a ContextAwaitable: + static_assert(sizeof(A) == 0, "requires ContextAwaitable"); // end::reject_plain[] } } @@ -271,14 +271,14 @@ void start_foreign_op(std::function on_complete) // Bridge a foreign async operation into a Capy coroutine. struct foreign_bridge { - capy::io_env const* env_ = nullptr; + capy::await_context const* env_ = nullptr; capy::continuation cont_; result_type result_; bool await_ready() const noexcept { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { env_ = env; cont_.h = h; // stable address; the executor links continuations intrusively @@ -304,7 +304,7 @@ capy::task use_bridge() co_return co_await foreign_bridge{}; } -struct io_awaitable_test +struct context_awaitable_test { void testForeignBridge() { @@ -323,7 +323,7 @@ struct io_awaitable_test { // tag::run_awaitable[] // Run the task on a thread pool and observe the value the - // custom IoAwaitable delivered to the completion handler. + // custom ContextAwaitable delivered to the completion handler. capy::thread_pool pool(1); int result = 0; capy::run_async(pool.get_executor(), [&result](int value) { @@ -343,4 +343,4 @@ struct io_awaitable_test } // namespace -TEST_SUITE(io_awaitable_test, "boost.capy.doc.4d_io_awaitable"); +TEST_SUITE(context_awaitable_test, "boost.capy.doc.4d_context_awaitable"); diff --git a/test/doc/snippets/4f_cancellation.cpp b/test/doc/snippets/4f_cancellation.cpp index 9c69f0474..986f637f8 100644 --- a/test/doc/snippets/4f_cancellation.cpp +++ b/test/doc/snippets/4f_cancellation.cpp @@ -16,12 +16,12 @@ #include #include #include +#include #include -#include #include #include #include -#include +#include #include #include @@ -95,7 +95,7 @@ capy::task<> parent() capy::task<> child() { - // Receives parent's stop token via IoAwaitable protocol + // Receives parent's stop token via ContextAwaitable protocol auto token = co_await capy::this_coro::stop_token; // Access current token } // end::token_propagation[] @@ -175,7 +175,7 @@ capy::task fetch_with_cancel() std::optional>> stop_cb; std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { stop_cb.emplace(env->stop_token, h); // Resumes inline! return std::noop_coroutine(); @@ -185,9 +185,9 @@ std::coroutine_handle<> await_suspend( // Compile-only bug demo: firing the callback would resume the // coroutine on the stopping thread, so nothing ever calls it. [[maybe_unused]] std::coroutine_handle<> (* const wrong_pattern)( - std::coroutine_handle<>, capy::io_env const*) = &await_suspend; + std::coroutine_handle<>, capy::await_context const*) = &await_suspend; -void start_async_operation(std::coroutine_handle<>, capy::io_env const*) {} +void start_async_operation(std::coroutine_handle<>, capy::await_context const*) {} // tag::stoppable_awaitable[] struct my_stoppable_awaitable @@ -201,7 +201,7 @@ struct my_stoppable_awaitable bool await_ready() { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> h, capy::io_env const* env) + std::coroutine_handle<> h, capy::await_context const* env) { if (env->stop_token.stop_requested()) return h; // Already cancelled @@ -235,23 +235,23 @@ struct fetch_channel // One side of the race: completes when the fetch worker thread // wakes fetch_ready. If the deadline wins first, when_any's stop // request cancels the wait; flag the worker so it stops early. -capy::io_task await_fetch(fetch_channel& ch) +capy::task> await_fetch(fetch_channel& ch) { auto [ec] = co_await ch.fetch_ready.wait(); if (ec) { ch.cancelled.store(true); - co_return capy::io_result{ec, {}}; + co_return capy::results{ec, {}}; } - co_return capy::io_result{std::error_code(), std::move(ch.result)}; + co_return capy::results{std::error_code(), std::move(ch.result)}; } // The other side of the race: completes once whatever plays the // clock wakes the waker. -capy::io_task<> deadline(capy::async_waker& waker) +capy::task> deadline(capy::async_waker& waker) { auto [ec] = co_await waker.wait(); - co_return capy::io_result<>{ec}; + co_return capy::results<>{ec}; } // end::racing_deadline[] @@ -275,7 +275,7 @@ class download_manager public: void start_download(std::string url) { - // Token propagated via io_env, not as a function argument + // Token propagated via await_context, not as a function argument capy::run_async(executor_, stop_source_.get_token())(download(url)); } @@ -287,7 +287,7 @@ class download_manager capy::task download(std::string url) { - // From run_async's io_env + // From run_async's await_context auto token = co_await capy::this_coro::stop_token; while (!token.stop_requested()) { diff --git a/test/doc/snippets/4g_composition.cpp b/test/doc/snippets/4g_composition.cpp index d1c36edca..391c16eff 100644 --- a/test/doc/snippets/4g_composition.cpp +++ b/test/doc/snippets/4g_composition.cpp @@ -21,7 +21,8 @@ #include #include -#include +#include +#include #include #include @@ -52,9 +53,9 @@ namespace overview { std::atomic runs{0}; -capy::io_task<> task_a() { ++runs; co_return capy::io_result<>{}; } -capy::io_task<> task_b() { ++runs; co_return capy::io_result<>{}; } -capy::io_task<> task_c() { ++runs; co_return capy::io_result<>{}; } +capy::task> task_a() { ++runs; co_return capy::results<>{}; } +capy::task> task_b() { ++runs; co_return capy::results<>{}; } +capy::task> task_c() { ++runs; co_return capy::results<>{}; } // tag::sequential[] capy::task<> sequential() @@ -79,19 +80,19 @@ namespace when_all_basics { // tag::when_all_basic[] -capy::io_task fetch_a() +capy::task> fetch_a() { - co_return capy::io_result{std::error_code(), 1}; + co_return capy::results{std::error_code(), 1}; } -capy::io_task fetch_b() +capy::task> fetch_b() { - co_return capy::io_result{std::error_code(), 2}; + co_return capy::results{std::error_code(), 2}; } -capy::io_task fetch_c() +capy::task> fetch_c() { - co_return capy::io_result{std::error_code(), "hello"}; + co_return capy::results{std::error_code(), "hello"}; } capy::task<> example() @@ -111,11 +112,11 @@ capy::task<> example() namespace void_mix { // tag::when_all_void_mix[] -capy::io_task<> void_task() { co_return capy::io_result<>{}; } +capy::task> void_task() { co_return capy::results<>{}; } -capy::io_task int_task() +capy::task> int_task() { - co_return capy::io_result{std::error_code(), 42}; + co_return capy::results{std::error_code(), 42}; } capy::task<> example() @@ -123,7 +124,7 @@ capy::task<> example() auto [ec, a, b, c] = co_await capy::when_all( int_task(), void_task(), int_task()); // a == 42 (int) - // b == tuple<> (from void io_task) + // b == tuple<> (from task>) // c == 42 (int) } // end::when_all_void_mix[] @@ -132,8 +133,8 @@ capy::task<> example() namespace all_void { -capy::io_task<> void_task_a() { co_return capy::io_result<>{}; } -capy::io_task<> void_task_b() { co_return capy::io_result<>{}; } +capy::task> void_task_a() { co_return capy::results<>{}; } +capy::task> void_task_b() { co_return capy::results<>{}; } // tag::when_all_all_void[] capy::task<> example() @@ -150,14 +151,14 @@ capy::task<> example() namespace error_handling { -capy::io_task task_a() +capy::task> task_a() { - co_return capy::io_result{std::error_code(), 1}; + co_return capy::results{std::error_code(), 1}; } -capy::io_task task_b() +capy::task> task_b() { - co_return capy::io_result{capy::error::timeout, 0}; + co_return capy::results{capy::error::timeout, 0}; } // tag::when_all_error[] @@ -174,11 +175,11 @@ capy::task<> example() namespace exceptions { // tag::when_all_exception[] -capy::io_task might_throw(bool fail) +capy::task> might_throw(bool fail) { if (fail) throw std::runtime_error("failed"); - co_return capy::io_result{std::error_code(), 42}; + co_return capy::results{std::error_code(), 42}; } capy::task<> example() @@ -200,30 +201,30 @@ namespace stop_prop { std::atomic iterations{0}; -capy::io_task<> do_iteration() +capy::task> do_iteration() { ++iterations; - co_return capy::io_result<>{}; + co_return capy::results<>{}; } -capy::io_task<> fail_fast() +capy::task> fail_fast() { - co_return capy::io_result<>{capy::error::timeout}; + co_return capy::results<>{capy::error::timeout}; } // tag::stop_propagation[] -capy::io_task<> long_running() +capy::task> long_running() { auto token = co_await capy::this_coro::stop_token; for (int i = 0; i < 1000; ++i) { if (token.stop_requested()) - co_return capy::io_result<>{}; // Exit early when sibling fails + co_return capy::results<>{}; // Exit early when sibling fails co_await do_iteration(); } - co_return capy::io_result<>{}; + co_return capy::results<>{}; } // end::stop_propagation[] @@ -231,14 +232,14 @@ capy::io_task<> long_running() namespace any_basic { -capy::io_task fetch_int() +capy::task> fetch_int() { - co_return capy::io_result{std::error_code(), 7}; + co_return capy::results{std::error_code(), 7}; } -capy::io_task fetch_string() +capy::task> fetch_string() { - co_return capy::io_result{std::error_code(), "s"}; + co_return capy::results{std::error_code(), "s"}; } // tag::when_any_basic[] @@ -246,8 +247,8 @@ capy::io_task fetch_string() capy::task<> example() { auto result = co_await capy::when_any( - fetch_int(), // io_task - fetch_string() // io_task + fetch_int(), // task> + fetch_string() // task> ); // result is std::variant // index 0: all tasks failed (error_code) @@ -260,16 +261,16 @@ capy::task<> example() namespace wrap_translate { -capy::io_task<> inner() { co_return capy::io_result<>{capy::error::canceled}; } +capy::task> inner() { co_return capy::results<>{capy::error::canceled}; } // tag::wrap_translate_error[] // canceled is benign here: translate it to success so when_any picks this child. -capy::io_task<> wrapped() +capy::task> wrapped() { auto [ec] = co_await inner(); if (ec == capy::cond::canceled) - co_return capy::io_result<>{}; // success: when_any sees a winner - co_return capy::io_result<>{ec}; // propagate other errors unchanged + co_return capy::results<>{}; // success: when_any sees a winner + co_return capy::results<>{ec}; // propagate other errors unchanged } // end::wrap_translate_error[] @@ -277,14 +278,14 @@ capy::io_task<> wrapped() namespace wrap_lift { -capy::io_task<> inner() { co_return capy::io_result<>{capy::error::timeout}; } +capy::task> inner() { co_return capy::results<>{capy::error::timeout}; } // tag::wrap_lift_error[] // Always succeeds; the winner's payload carries the original ec. -capy::io_task wrapped() +capy::task> wrapped() { auto [ec] = co_await inner(); - co_return capy::io_result{std::error_code(), ec}; + co_return capy::results{std::error_code(), ec}; } // when_any(wrapped(), ...) -> variant @@ -303,24 +304,24 @@ struct page_data std::string sidebar; }; -capy::io_task fetch_header(std::string url) +capy::task> fetch_header(std::string url) { - co_return capy::io_result{std::error_code(), url + ":header"}; + co_return capy::results{std::error_code(), url + ":header"}; } -capy::io_task fetch_body(std::string url) +capy::task> fetch_body(std::string url) { - co_return capy::io_result{std::error_code(), url + ":body"}; + co_return capy::results{std::error_code(), url + ":body"}; } -capy::io_task fetch_sidebar(std::string url) +capy::task> fetch_sidebar(std::string url) { - co_return capy::io_result{ + co_return capy::results{ std::error_code(), url + ":sidebar"}; } // tag::parallel_fetch[] -capy::io_task fetch_page_data(std::string url) +capy::task> fetch_page_data(std::string url) { auto [ec, header, body, sidebar] = co_await capy::when_all( fetch_header(url), @@ -328,9 +329,9 @@ capy::io_task fetch_page_data(std::string url) fetch_sidebar(url) ); if (ec) - co_return capy::io_result{ec, {}}; + co_return capy::results{ec, {}}; - co_return capy::io_result{std::error_code(), { + co_return capy::results{std::error_code(), { std::move(header), std::move(body), std::move(sidebar) @@ -348,11 +349,11 @@ struct item }; // tag::fan_out[] -capy::io_task process_item(item i); +capy::task> process_item(item i); capy::task process_all(std::vector const& items) { - std::vector> tasks; + std::vector>> tasks; for (auto const& item : items) tasks.push_back(process_item(item)); @@ -367,9 +368,9 @@ capy::task process_all(std::vector const& items) } // end::fan_out[] -capy::io_task process_item(item i) +capy::task> process_item(item i) { - co_return capy::io_result{std::error_code(), i.value}; + co_return capy::results{std::error_code(), i.value}; } } // namespace fanout diff --git a/test/doc/snippets/5b_composite_buffers.cpp b/test/doc/snippets/5b_composite_buffers.cpp index 9f853df47..f8a8592ff 100644 --- a/test/doc/snippets/5b_composite_buffers.cpp +++ b/test/doc/snippets/5b_composite_buffers.cpp @@ -30,9 +30,9 @@ #include #include #include -#include -#include +#include #include +#include #include #include #include @@ -188,7 +188,7 @@ struct chained_buffers // The iteration fragment binds each buffer without using it; the page // comment explains the loop body instead. The slice fragment discards -// io_result values the same way. +// results values the same way. // tag::iterate[] template diff --git a/test/doc/snippets/5c_system_io_integration.cpp b/test/doc/snippets/5c_system_io_integration.cpp index 987ef1fb0..1e800a394 100644 --- a/test/doc/snippets/5c_system_io_integration.cpp +++ b/test/doc/snippets/5c_system_io_integration.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include @@ -73,7 +73,7 @@ using namespace std::string_view_literals; // tag::write_some_signature[] template -capy::io_task write_some(Buffers buffers); +capy::task> write_some(Buffers buffers); // end::write_some_signature[] #if defined(BOOST_CAPY_DOC_HAS_POSIX_IO) diff --git a/test/doc/snippets/5d_buffer_algo.cpp b/test/doc/snippets/5d_buffer_algo.cpp index 28fd87226..8e950d7cc 100644 --- a/test/doc/snippets/5d_buffer_algo.cpp +++ b/test/doc/snippets/5d_buffer_algo.cpp @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/test/doc/snippets/6a_concepts.cpp b/test/doc/snippets/6a_concepts.cpp index 33555f367..4dc1e4c8d 100644 --- a/test/doc/snippets/6a_concepts.cpp +++ b/test/doc/snippets/6a_concepts.cpp @@ -20,8 +20,7 @@ #include #include #include -#include -#include +#include #include #include #include @@ -111,10 +110,10 @@ class string_source } template - capy::io_task + capy::task> read_some(MB buffers) { - using result = capy::io_result; + using result = capy::results; if(rest_.empty()) co_return result{capy::error::eof, 0}; @@ -145,10 +144,10 @@ static_assert(capy::Stream); // Algorithms name the concept rather than a concrete stream, so the same // code drives a socket, a TLS stream, or the test double. template -capy::io_task +capy::task> count_bytes(S& source) { - using result = capy::io_result; + using result = capy::results; char storage[64]; std::size_t total = 0; diff --git a/test/doc/snippets/6b_wrappers.cpp b/test/doc/snippets/6b_wrappers.cpp index 4786c92d7..a503514ff 100644 --- a/test/doc/snippets/6b_wrappers.cpp +++ b/test/doc/snippets/6b_wrappers.cpp @@ -89,7 +89,7 @@ struct MyReadStream { MyReadStream() = default; MyReadStream(MyReadStream&&) = default; - capy::io_task read_some(auto&&) { co_return {std::error_code{}, 0}; } + capy::task> read_some(auto&&) { co_return {std::error_code{}, 0}; } }; MyReadStream makeMyStream() { return {}; } diff --git a/test/doc/snippets/7a_drivers.cpp b/test/doc/snippets/7a_drivers.cpp index 73d2ac5e4..89a58a9e7 100644 --- a/test/doc/snippets/7a_drivers.cpp +++ b/test/doc/snippets/7a_drivers.cpp @@ -36,7 +36,6 @@ namespace { // headers are already included above); they are kept for the page text. // tag::run_blocking_basic[] -#include #include namespace capy = boost::capy; diff --git a/test/doc/snippets/7b_mock_streams.cpp b/test/doc/snippets/7b_mock_streams.cpp index 11dc339d9..9727eecf1 100644 --- a/test/doc/snippets/7b_mock_streams.cpp +++ b/test/doc/snippets/7b_mock_streams.cpp @@ -43,7 +43,6 @@ namespace { #include #include #include -#include namespace capy = boost::capy; @@ -95,7 +94,6 @@ void test_write_stream() #include #include #include -#include namespace capy = boost::capy; diff --git a/test/doc/snippets/7e_buffer_inspection.cpp b/test/doc/snippets/7e_buffer_inspection.cpp index 92921a88f..0f2989b1e 100644 --- a/test/doc/snippets/7e_buffer_inspection.cpp +++ b/test/doc/snippets/7e_buffer_inspection.cpp @@ -38,7 +38,6 @@ namespace { #include #include #include -#include namespace capy = boost::capy; diff --git a/test/doc/snippets/9c_read_stream.cpp b/test/doc/snippets/9c_read_stream.cpp index 03b122eaf..995185546 100644 --- a/test/doc/snippets/9c_read_stream.cpp +++ b/test/doc/snippets/9c_read_stream.cpp @@ -18,8 +18,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -44,7 +44,7 @@ namespace composed { // tag::read_signature[] auto read(capy::ReadStream auto& stream, capy::MutableBufferSequence auto buffers) - -> capy::io_task; + -> capy::task>; // end::read_signature[] } // namespace composed diff --git a/test/doc/snippets/9f_write_stream.cpp b/test/doc/snippets/9f_write_stream.cpp index a328be98c..af4beee79 100644 --- a/test/doc/snippets/9f_write_stream.cpp +++ b/test/doc/snippets/9f_write_stream.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -46,7 +46,7 @@ namespace api_sketch { // tag::write_signature[] auto write(capy::WriteStream auto& stream, capy::ConstBufferSequence auto buffers) - -> capy::io_task; + -> capy::task>; // end::write_signature[] // tag::write_now_sketch[] @@ -56,14 +56,14 @@ class write_now public: explicit write_now(Stream& s) noexcept; - capy::IoAwaitable auto operator()(capy::ConstBufferSequence auto buffers); + capy::ContextAwaitable auto operator()(capy::ConstBufferSequence auto buffers); }; // end::write_now_sketch[] } // namespace api_sketch static_assert(requires(capy::test::write_stream& s, capy::const_buffer b) { - { capy::write(s, b) } -> std::same_as>; + { capy::write(s, b) } -> std::same_as>>; }); static_assert( std::constructible_from< diff --git a/test/doc/snippets/9k_executor.cpp b/test/doc/snippets/9k_executor.cpp index 3576d9ce7..975f7245f 100644 --- a/test/doc/snippets/9k_executor.cpp +++ b/test/doc/snippets/9k_executor.cpp @@ -15,9 +15,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -133,7 +133,7 @@ static_assert(sizeof(layout_sketch::executor_ref) // I/O awaitable shape: embeds a continuation for the caller's handle // and an executor_ref for completion dispatch. -struct io_awaitable_sketch +struct context_awaitable_sketch { capy::continuation cont_; capy::executor_ref ex_; @@ -142,7 +142,7 @@ struct io_awaitable_sketch std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - capy::io_env const* env) noexcept + capy::await_context const* env) noexcept { cont_.h = h; ex_ = env->executor; diff --git a/test/doc/snippets/9n_why_not_cobalt.cpp b/test/doc/snippets/9n_why_not_cobalt.cpp index 22c0316bd..872c0342b 100644 --- a/test/doc/snippets/9n_why_not_cobalt.cpp +++ b/test/doc/snippets/9n_why_not_cobalt.cpp @@ -12,7 +12,7 @@ #include "../doc_warnings.hpp" -#include +#include #include @@ -20,11 +20,11 @@ namespace capy = boost::capy; namespace { -// Shows the IoAwaitable protocol signature used for context propagation. +// Shows the ContextAwaitable protocol signature used for context propagation. struct context_propagating_op { // tag::await_suspend_env[] - auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env); + auto await_suspend(std::coroutine_handle<> h, capy::await_context const* env); // end::await_suspend_env[] }; diff --git a/test/doc/snippets/9o_why_not_cobalt_concepts.cpp b/test/doc/snippets/9o_why_not_cobalt_concepts.cpp index 5ff512073..8e82c285b 100644 --- a/test/doc/snippets/9o_why_not_cobalt_concepts.cpp +++ b/test/doc/snippets/9o_why_not_cobalt_concepts.cpp @@ -13,8 +13,8 @@ #include "../doc_warnings.hpp" #include -#include -#include +#include +#include #include #include #include @@ -37,9 +37,9 @@ namespace { capy::task<> my_algo(capy::any_write_stream& stream); // end::capy_signature[] -static_assert(capy::IoAwaitable>); -static_assert(capy::IoRunnable>); -static_assert(capy::IoRunnable>); +static_assert(capy::ContextAwaitable>); +static_assert(capy::ContextRunnable>); +static_assert(capy::ContextRunnable>); namespace context_propagation { @@ -47,7 +47,7 @@ namespace context_propagation { struct child_operation { // tag::await_suspend_env[] - auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env); + auto await_suspend(std::coroutine_handle<> h, capy::await_context const* env); // end::await_suspend_env[] }; diff --git a/test/doc/snippets/9p_why_not_tmc.cpp b/test/doc/snippets/9p_why_not_tmc.cpp index f6c60a4c6..ab4edaa76 100644 --- a/test/doc/snippets/9p_why_not_tmc.cpp +++ b/test/doc/snippets/9p_why_not_tmc.cpp @@ -13,8 +13,8 @@ #include "../doc_warnings.hpp" #include -#include -#include +#include +#include #include #include @@ -54,28 +54,28 @@ struct plain_awaitable_handle // end::std_suspend_handle[] }; -struct io_awaitable_form +struct context_awaitable_form { // tag::await_suspend_env[] - auto await_suspend(std::coroutine_handle<> h, capy::io_env const* env); + auto await_suspend(std::coroutine_handle<> h, capy::await_context const* env); // end::await_suspend_env[] }; // Mirrors the shape of task.hpp's transform_awaitable(); never -// instantiated with a non-IoAwaitable, so the else branch only +// instantiated with a non-ContextAwaitable, so the else branch only // has to parse. template void protocol_strictness_shape() { using A = std::decay_t; - if constexpr (capy::IoAwaitable) + if constexpr (capy::ContextAwaitable) { } // tag::strict_static_assert[] // From task.hpp transform_awaitable() else { - static_assert(sizeof(A) == 0, "requires IoAwaitable"); + static_assert(sizeof(A) == 0, "requires ContextAwaitable"); } // end::strict_static_assert[] } diff --git a/test/unit/concept/io_awaitable.cpp b/test/unit/concept/context_awaitable.cpp similarity index 74% rename from test/unit/concept/io_awaitable.cpp rename to test/unit/concept/context_awaitable.cpp index 9642c481b..13da2d581 100644 --- a/test/unit/concept/io_awaitable.cpp +++ b/test/unit/concept/context_awaitable.cpp @@ -8,7 +8,7 @@ // // Test that header file is self-contained. -#include +#include #include #include @@ -31,7 +31,7 @@ struct awaitable_suspend_void void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -44,7 +44,7 @@ struct awaitable_suspend_bool bool await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { return false; } @@ -58,7 +58,7 @@ struct awaitable_suspend_handle std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const*) const noexcept + await_context const*) const noexcept { return h; } @@ -72,7 +72,7 @@ struct awaitable_suspend_typed_handle std::coroutine_handle await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { return {}; } @@ -87,7 +87,7 @@ struct awaitable_suspend_int int await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { return 0; } @@ -112,7 +112,7 @@ struct awaitable_ready_void void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -128,7 +128,7 @@ struct awaitable_no_move void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -155,27 +155,27 @@ static_assert(!AwaitSuspendResult); static_assert(!AwaitSuspendResult); static_assert(!AwaitSuspendResult*>); -// Every valid await_suspend return type satisfies IoAwaitable -static_assert(IoAwaitable); -static_assert(IoAwaitable); -static_assert(IoAwaitable); -static_assert(IoAwaitable); +// Every valid await_suspend return type satisfies ContextAwaitable +static_assert(ContextAwaitable); +static_assert(ContextAwaitable); +static_assert(ContextAwaitable); +static_assert(ContextAwaitable); // Invalid protocol shapes are rejected -static_assert(!IoAwaitable); -static_assert(!IoAwaitable); -static_assert(!IoAwaitable); -static_assert(!IoAwaitable); -static_assert(!IoAwaitable); +static_assert(!ContextAwaitable); +static_assert(!ContextAwaitable); +static_assert(!ContextAwaitable); +static_assert(!ContextAwaitable); +static_assert(!ContextAwaitable); // awaitable_result_t names the await_resume return type static_assert(std::same_as, void>); static_assert(std::same_as, int>); -// IoAwaitableRange requires a sized input range of IoAwaitables -static_assert(IoAwaitableRange>); -static_assert(!IoAwaitableRange>); -static_assert(!IoAwaitableRange); +// ContextAwaitableRange requires a sized input range of ContextAwaitables +static_assert(ContextAwaitableRange>); +static_assert(!ContextAwaitableRange>); +static_assert(!ContextAwaitableRange); } // namespace capy } // namespace boost diff --git a/test/unit/concept/read_stream.cpp b/test/unit/concept/read_stream.cpp index 11ac74857..c0e1811c7 100644 --- a/test/unit/concept/read_stream.cpp +++ b/test/unit/concept/read_stream.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include @@ -23,14 +23,14 @@ namespace capy { namespace { -// Mock IoAwaitable returning std::pair +// Mock ContextAwaitable returning std::pair struct mock_read_awaitable_pair { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -41,14 +41,14 @@ struct mock_read_awaitable_pair } }; -// Mock IoAwaitable returning std::tuple +// Mock ContextAwaitable returning std::tuple struct mock_read_awaitable_tuple { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -59,28 +59,28 @@ struct mock_read_awaitable_tuple } }; -// Mock IoAwaitable with wrong return type +// Mock ContextAwaitable with wrong return type struct mock_read_awaitable_wrong_type { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } int await_resume() const noexcept { return 0; } }; -// Mock IoAwaitable with wrong decomposition order +// Mock ContextAwaitable with wrong decomposition order struct mock_read_awaitable_wrong_order { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -91,7 +91,7 @@ struct mock_read_awaitable_wrong_order } }; -// Mock awaitable missing IoAwaitable protocol +// Mock awaitable missing ContextAwaitable protocol struct mock_read_awaitable_not_io { bool await_ready() const noexcept { return true; } @@ -163,7 +163,7 @@ struct invalid_read_stream_wrong_order } }; -// Invalid: not an IoAwaitable +// Invalid: not a ContextAwaitable struct invalid_read_stream_not_io { template @@ -205,7 +205,7 @@ static_assert(!ReadStream); // Wrong decomposition order does not satisfy ReadStream static_assert(!ReadStream); -// Non-IoAwaitable does not satisfy ReadStream +// Non-ContextAwaitable does not satisfy ReadStream static_assert(!ReadStream); // Missing read_some does not satisfy ReadStream diff --git a/test/unit/concept/stream.cpp b/test/unit/concept/stream.cpp index d5e7e23cc..f99907f49 100644 --- a/test/unit/concept/stream.cpp +++ b/test/unit/concept/stream.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include #include @@ -28,14 +28,14 @@ namespace { // Mock awaitables //---------------------------------------------------------- -// Mock IoAwaitable returning std::pair for read +// Mock ContextAwaitable returning std::pair for read struct mock_read_awaitable { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -46,14 +46,14 @@ struct mock_read_awaitable } }; -// Mock IoAwaitable returning std::pair for write +// Mock ContextAwaitable returning std::pair for write struct mock_write_awaitable { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } diff --git a/test/unit/concept/write_stream.cpp b/test/unit/concept/write_stream.cpp index ca71be595..1b9948f69 100644 --- a/test/unit/concept/write_stream.cpp +++ b/test/unit/concept/write_stream.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include @@ -23,14 +23,14 @@ namespace capy { namespace { -// Mock IoAwaitable returning std::pair +// Mock ContextAwaitable returning std::pair struct mock_write_awaitable_pair { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -41,14 +41,14 @@ struct mock_write_awaitable_pair } }; -// Mock IoAwaitable returning std::tuple +// Mock ContextAwaitable returning std::tuple struct mock_write_awaitable_tuple { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -59,28 +59,28 @@ struct mock_write_awaitable_tuple } }; -// Mock IoAwaitable with wrong return type +// Mock ContextAwaitable with wrong return type struct mock_write_awaitable_wrong_type { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } int await_resume() const noexcept { return 0; } }; -// Mock IoAwaitable with wrong decomposition order +// Mock ContextAwaitable with wrong decomposition order struct mock_write_awaitable_wrong_order { bool await_ready() const noexcept { return true; } void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } @@ -91,7 +91,7 @@ struct mock_write_awaitable_wrong_order } }; -// Mock awaitable missing IoAwaitable protocol +// Mock awaitable missing ContextAwaitable protocol struct mock_write_awaitable_not_io { bool await_ready() const noexcept { return true; } @@ -163,7 +163,7 @@ struct invalid_write_stream_wrong_order } }; -// Invalid: not an IoAwaitable +// Invalid: not a ContextAwaitable struct invalid_write_stream_not_io { template @@ -205,7 +205,7 @@ static_assert(!WriteStream); // Wrong decomposition order does not satisfy WriteStream static_assert(!WriteStream); -// Non-IoAwaitable does not satisfy WriteStream +// Non-ContextAwaitable does not satisfy WriteStream static_assert(!WriteStream); // Missing write_some does not satisfy WriteStream diff --git a/test/unit/custom_task.hpp b/test/unit/custom_task.hpp index e37849140..0147e431a 100644 --- a/test/unit/custom_task.hpp +++ b/test/unit/custom_task.hpp @@ -10,9 +10,9 @@ #ifndef BOOST_CAPY_TEST_CUSTOM_TASK_HPP #define BOOST_CAPY_TEST_CUSTOM_TASK_HPP -#include -#include -#include +#include +#include +#include #include #include @@ -40,16 +40,16 @@ struct custom_task_result_base void return_void() {} }; -/** A custom task type that satisfies IoRunnable. +/** A custom task type that satisfies ContextRunnable. This task is intentionally NOT capy::task to prove that - run_async and run work with any IoRunnable type. + run_async and run work with any ContextRunnable type. */ template struct custom_task { struct promise_type - : io_awaitable_promise_base + : context_awaitable_promise_base , custom_task_result_base { std::exception_ptr ep_; @@ -109,7 +109,7 @@ struct custom_task return std::move(*h_.promise().result_); } - std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> cont, await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(*env); @@ -120,8 +120,8 @@ struct custom_task void release() noexcept { h_ = nullptr; } }; -static_assert(IoRunnable>); -static_assert(IoRunnable>); +static_assert(ContextRunnable>); +static_assert(ContextRunnable>); } // namespace test } // namespace capy diff --git a/test/unit/death_test.hpp b/test/unit/death_test.hpp index 4ca21be7c..bbb4a2244 100644 --- a/test/unit/death_test.hpp +++ b/test/unit/death_test.hpp @@ -12,7 +12,7 @@ #if !defined(_WIN32) -#include +#include #include #include @@ -82,7 +82,7 @@ exits_cleanly(F fn) /// Start a task by hand, as an awaiting parent would, and leave it lent. template void -lend(Task& t, io_env& env) +lend(Task& t, await_context& env) { t.await_suspend(std::noop_coroutine(), &env).resume(); } diff --git a/test/unit/detail/await_suspend_helper.cpp b/test/unit/detail/await_suspend_helper.cpp index 70b6eec96..e33236662 100644 --- a/test/unit/detail/await_suspend_helper.cpp +++ b/test/unit/detail/await_suspend_helper.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include @@ -26,7 +26,7 @@ class await_suspend_helper_test struct void_awaitable { bool suspended = false; - void await_suspend(std::coroutine_handle<>, io_env const*) + void await_suspend(std::coroutine_handle<>, await_context const*) { suspended = true; } @@ -36,7 +36,7 @@ class await_suspend_helper_test struct bool_awaitable { bool value; - bool await_suspend(std::coroutine_handle<>, io_env const*) + bool await_suspend(std::coroutine_handle<>, await_context const*) { return value; } @@ -48,7 +48,7 @@ class await_suspend_helper_test { std::coroutine_handle

next; std::coroutine_handle<> - await_suspend(std::coroutine_handle<>, io_env const*) + await_suspend(std::coroutine_handle<>, await_context const*) { return next; } diff --git a/test/unit/ex/abandon.cpp b/test/unit/ex/abandon.cpp index 7cc74424a..ddcf785f5 100644 --- a/test/unit/ex/abandon.cpp +++ b/test/unit/ex/abandon.cpp @@ -10,9 +10,9 @@ // Test that header file is self-contained. #include +#include +#include #include -#include -#include #include #include #include @@ -65,7 +65,7 @@ struct plain_park struct own_delete_task { struct promise_type - : io_awaitable_promise_base + : context_awaitable_promise_base { static void* operator new(std::size_t size) { @@ -146,7 +146,7 @@ struct own_delete_task bool await_ready() const noexcept { return false; } std::coroutine_handle<> await_suspend( - std::coroutine_handle<> cont, io_env const* env) + std::coroutine_handle<> cont, await_context const* env) { h_.promise().set_continuation(cont); h_.promise().set_environment(env); @@ -210,7 +210,7 @@ struct parking_lot bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) + await_suspend(std::coroutine_handle<> h, await_context const* env) { lot->hs.push_back(continuation{h, nullptr, env->owner}); return std::noop_coroutine(); @@ -253,7 +253,7 @@ struct signal_park bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { slot->h = h; slot->owner = env->owner; diff --git a/test/unit/ex/async_event.cpp b/test/unit/ex/async_event.cpp index 61faf5fb5..10a733b05 100644 --- a/test/unit/ex/async_event.cpp +++ b/test/unit/ex/async_event.cpp @@ -10,16 +10,15 @@ // Test that header file is self-contained. #include -#include +#include #include #include #include #include -#include +#include #include #include -#include -#include +#include #include #include @@ -36,7 +35,7 @@ namespace boost { namespace capy { -static_assert(IoAwaitable); +static_assert(ContextAwaitable); struct async_event_test { @@ -91,7 +90,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); @@ -138,7 +137,7 @@ struct async_event_test t2.release(); t3.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); h2.promise().set_environment(&env); @@ -182,7 +181,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); @@ -209,7 +208,7 @@ struct async_event_test auto h1 = t1.handle(); t1.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); h1.resume(); @@ -276,7 +275,7 @@ struct async_event_test t2.release(); t3.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); h2.promise().set_environment(&env); @@ -332,7 +331,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -383,14 +382,14 @@ struct async_event_test auto h1 = w1.handle(); w1.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); env1.stop_token = ss.get_token(); h1.promise().set_environment(&env1); auto h2 = w2.handle(); w2.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); h2.promise().set_environment(&env2); @@ -446,7 +445,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -480,7 +479,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -506,7 +505,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); @@ -541,7 +540,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -584,7 +583,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); @@ -627,7 +626,7 @@ struct async_event_test t2.release(); t3.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); h2.promise().set_environment(&env); @@ -669,7 +668,7 @@ struct async_event_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -686,11 +685,11 @@ struct async_event_test h.destroy(); } - static io_task<> + static task> set_event_task(async_event& evt) { evt.set(); - co_return io_result<>{}; + co_return results<>{}; } static task diff --git a/test/unit/ex/async_mutex.cpp b/test/unit/ex/async_mutex.cpp index dad7253a0..258143d66 100644 --- a/test/unit/ex/async_mutex.cpp +++ b/test/unit/ex/async_mutex.cpp @@ -10,12 +10,12 @@ // Test that header file is self-contained. #include -#include +#include #include #include #include -#include -#include +#include +#include #include #include #include @@ -36,8 +36,8 @@ namespace boost { namespace capy { -static_assert(IoAwaitable); -static_assert(IoAwaitable); +static_assert(ContextAwaitable); +static_assert(ContextAwaitable); struct async_mutex_test { @@ -59,7 +59,7 @@ struct async_mutex_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); @@ -94,7 +94,7 @@ struct async_mutex_test auto h1 = first.handle(); first.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); @@ -149,13 +149,13 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = waiter.handle(); waiter.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); @@ -210,13 +210,13 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = waiter.handle(); waiter.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); @@ -266,7 +266,7 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); @@ -330,27 +330,27 @@ struct async_mutex_test auto hh = holder.handle(); holder.release(); - io_env env_h; + await_context env_h; env_h.executor = executor_ref(ex); hh.promise().set_environment(&env_h); auto h1 = w1.handle(); w1.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); env1.stop_token = ss1.get_token(); h1.promise().set_environment(&env1); auto h2 = w2.handle(); w2.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss2.get_token(); h2.promise().set_environment(&env2); auto h3 = w3.handle(); w3.release(); - io_env env3; + await_context env3; env3.executor = executor_ref(ex); h3.promise().set_environment(&env3); @@ -434,20 +434,20 @@ struct async_mutex_test auto hh = holder.handle(); holder.release(); - io_env env_h; + await_context env_h; env_h.executor = executor_ref(ex); hh.promise().set_environment(&env_h); auto h1 = w1.handle(); w1.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); env1.stop_token = ss1.get_token(); h1.promise().set_environment(&env1); auto h2 = w2.handle(); w2.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); h2.promise().set_environment(&env2); @@ -512,13 +512,13 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = waiter.handle(); waiter.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); @@ -558,7 +558,7 @@ struct async_mutex_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -589,7 +589,7 @@ struct async_mutex_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); @@ -626,13 +626,13 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = waiter.handle(); waiter.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); @@ -690,7 +690,7 @@ struct async_mutex_test auto hh = holder.handle(); holder.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); hh.promise().set_environment(&env); @@ -770,26 +770,26 @@ struct async_mutex_test auto hh = holder.handle(); holder.release(); - io_env env_h; + await_context env_h; env_h.executor = executor_ref(ex); hh.promise().set_environment(&env_h); auto h1 = w1.handle(); w1.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = w2.handle(); w2.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); auto h3 = w3.handle(); w3.release(); - io_env env3; + await_context env3; env3.executor = executor_ref(ex); h3.promise().set_environment(&env3); @@ -848,7 +848,7 @@ struct async_mutex_test auto hh = holder.handle(); holder.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); hh.promise().set_environment(&env); @@ -894,7 +894,7 @@ struct async_mutex_test auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); @@ -931,7 +931,7 @@ struct async_mutex_test auto h1 = first.handle(); first.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h1.promise().set_environment(&env); @@ -984,13 +984,13 @@ struct async_mutex_test auto h1 = holder.handle(); holder.release(); - io_env env1; + await_context env1; env1.executor = executor_ref(ex); h1.promise().set_environment(&env1); auto h2 = waiter.handle(); waiter.release(); - io_env env2; + await_context env2; env2.executor = executor_ref(ex); env2.stop_token = ss.get_token(); h2.promise().set_environment(&env2); diff --git a/test/unit/ex/async_waker.cpp b/test/unit/ex/async_waker.cpp index 44f05d914..5dea31a4c 100644 --- a/test/unit/ex/async_waker.cpp +++ b/test/unit/ex/async_waker.cpp @@ -365,7 +365,7 @@ struct async_waker_test }(waker, destroy_count_t(down)); auto h = t.handle(); t.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); h.promise().set_environment(&env); h.resume(); diff --git a/test/unit/ex/io_awaitable_promise_base.cpp b/test/unit/ex/context_awaitable_promise_base.cpp similarity index 91% rename from test/unit/ex/io_awaitable_promise_base.cpp rename to test/unit/ex/context_awaitable_promise_base.cpp index c0dd3f356..8b5976fd1 100644 --- a/test/unit/ex/io_awaitable_promise_base.cpp +++ b/test/unit/ex/context_awaitable_promise_base.cpp @@ -8,9 +8,9 @@ // // Test that header file is self-contained. -#include +#include -#include +#include #include #include "test_suite.hpp" @@ -24,7 +24,7 @@ namespace capy { struct test_coro { - struct promise_type : io_awaitable_promise_base + struct promise_type : context_awaitable_promise_base { test_coro get_return_object() { @@ -63,7 +63,7 @@ struct test_coro struct custom_transform_coro { - struct promise_type : io_awaitable_promise_base + struct promise_type : context_awaitable_promise_base { int transform_count_ = 0; @@ -109,13 +109,13 @@ struct custom_transform_coro } }; -struct io_awaitable_promise_base_test +struct context_awaitable_promise_base_test { void testSetAndGetEnvironment() { std::stop_source source; - io_env env; + await_context env; env.stop_token = source.get_token(); auto c = []() -> test_coro { co_return; }(); @@ -133,7 +133,7 @@ struct io_awaitable_promise_base_test testDefaultEnvironment() { auto c = []() -> test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto const* retrieved = c.h_.promise().environment(); @@ -148,7 +148,7 @@ struct io_awaitable_promise_base_test auto c = []() -> test_coro { co_return; }(); std::stop_source source; - io_env env; + await_context env; env.stop_token = source.get_token(); c.h_.promise().set_environment(&env); @@ -165,7 +165,7 @@ struct io_awaitable_promise_base_test { auto c = []() -> custom_transform_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); BOOST_TEST_EQ(c.h_.promise().transform_count_, 0); @@ -198,7 +198,7 @@ struct io_awaitable_promise_base_test testEnvironmentAwaiterNeverSuspends() { auto c = []() -> test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto awaiter = c.h_.promise().await_transform(this_coro::environment); @@ -213,7 +213,7 @@ struct io_awaitable_promise_base_test auto executor = pool.get_executor(); auto c = []() -> test_coro { co_return; }(); - io_env env; + await_context env; env.executor = executor_ref(executor); c.h_.promise().set_environment(&env); @@ -235,8 +235,8 @@ struct io_awaitable_promise_base_test }; TEST_SUITE( - io_awaitable_promise_base_test, - "boost.capy.ex.io_awaitable_promise_base"); + context_awaitable_promise_base_test, + "boost.capy.ex.context_awaitable_promise_base"); } // namespace capy } // namespace boost diff --git a/test/unit/ex/frame_allocator.cpp b/test/unit/ex/frame_allocator.cpp index 82d755827..7f69ead81 100644 --- a/test/unit/ex/frame_allocator.cpp +++ b/test/unit/ex/frame_allocator.cpp @@ -36,7 +36,7 @@ Allocation Flow - For standard Allocator: wraps it in frame_memory_resource stored in promise - Calls set_current_frame_allocator(&resource) (or mr directly) 3. Trampoline resumes the user's task -4. task::promise_type (via io_awaitable_promise_base) provides operator new/delete +4. task::promise_type (via context_awaitable_promise_base) provides operator new/delete that call get_current_frame_allocator()->allocate/deallocate 5. task::initial_suspend captures TLS into promise via set_frame_allocator() 6. task::initial_suspend::await_resume restores TLS when body starts @@ -58,7 +58,7 @@ recycling_memory_resource Default allocator with thread-local and global pools for frame recycling. Accessed via get_recycling_memory_resource() which returns pointer to static. -io_awaitable_promise_base +context_awaitable_promise_base CRTP mixin providing: - operator new/delete using get_current_frame_allocator() - alloc_ member to store captured allocator pointer diff --git a/test/unit/ex/frame_cb.cpp b/test/unit/ex/frame_cb.cpp index 60c8aa16e..9f72f67da 100644 --- a/test/unit/ex/frame_cb.cpp +++ b/test/unit/ex/frame_cb.cpp @@ -7,13 +7,13 @@ // Official repository: https://github.com/cppalliance/capy // -#include +#include #include #include -#include +#include #include #include -#include +#include #include #include @@ -84,7 +84,7 @@ struct frame_cb_test BOOST_TEST_EQ(value, 42); } - // IoAwaitable that resumes synchronously and returns a value + // ContextAwaitable that resumes synchronously and returns a value struct sync_awaitable { int value; @@ -97,18 +97,18 @@ struct frame_cb_test std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const*) noexcept + await_context const*) noexcept { return h; } - io_result await_resume() noexcept + results await_resume() noexcept { return {std::error_code{}, value}; } }; - static_assert(IoAwaitable); + static_assert(ContextAwaitable); static task await_sync(int v) @@ -118,7 +118,7 @@ struct frame_cb_test } void - testWithIoAwaitable() + testWithContextAwaitable() { int result = 0; test::run_blocking( @@ -127,7 +127,7 @@ struct frame_cb_test BOOST_TEST_EQ(result, 99); } - // IoAwaitable that posts to executor (async resume) + // ContextAwaitable that posts to executor (async resume) struct async_awaitable { int value; @@ -141,20 +141,20 @@ struct frame_cb_test std::coroutine_handle<> await_suspend( std::coroutine_handle<> h, - io_env const* env) noexcept + await_context const* env) noexcept { cont_.h = h; env->executor.post(cont_); return std::noop_coroutine(); } - io_result await_resume() noexcept + results await_resume() noexcept { return {std::error_code{}, value}; } }; - static_assert(IoAwaitable); + static_assert(ContextAwaitable); static task await_async(int v) @@ -190,7 +190,7 @@ struct frame_cb_test testResumeCallsFunctionPointer(); testDestroyIsNoOp(); testDataPointerPassedThrough(); - testWithIoAwaitable(); + testWithContextAwaitable(); testWithAsyncAwaitable(); } }; diff --git a/test/unit/ex/get_executor.cpp b/test/unit/ex/get_executor.cpp index 2063ec0df..fe5b30a68 100644 --- a/test/unit/ex/get_executor.cpp +++ b/test/unit/ex/get_executor.cpp @@ -10,8 +10,8 @@ // Test that header file is self-contained. #include -#include -#include +#include +#include #include #include "test_suite.hpp" @@ -24,7 +24,7 @@ namespace capy { struct tag_test_coro { - struct promise_type : io_awaitable_promise_base + struct promise_type : context_awaitable_promise_base { tag_test_coro get_return_object() { @@ -150,7 +150,7 @@ struct this_coro_tags_test auto executor = pool.get_executor(); auto c = []() -> tag_test_coro { co_return; }(); - io_env env; + await_context env; env.executor = executor_ref(executor); c.h_.promise().set_environment(&env); @@ -170,7 +170,7 @@ struct this_coro_tags_test auto c = []() -> tag_test_coro { co_return; }(); std::stop_source source; - io_env env; + await_context env; env.stop_token = source.get_token(); c.h_.promise().set_environment(&env); @@ -194,7 +194,7 @@ struct this_coro_tags_test auto c = []() -> tag_test_coro { co_return; }(); auto* mr = std::pmr::new_delete_resource(); - io_env env; + await_context env; env.frame_allocator = mr; c.h_.promise().set_environment(&env); @@ -211,7 +211,7 @@ struct this_coro_tags_test testAwaitTransformAllocatorDefaultIsNull() { auto c = []() -> tag_test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto awaiter = c.h_.promise().await_transform(this_coro::frame_allocator); diff --git a/test/unit/ex/get_stop_token.cpp b/test/unit/ex/get_stop_token.cpp index 8bfb95d4a..8863db4ac 100644 --- a/test/unit/ex/get_stop_token.cpp +++ b/test/unit/ex/get_stop_token.cpp @@ -10,8 +10,8 @@ // Test that header file is self-contained. #include -#include -#include +#include +#include #include "test_suite.hpp" @@ -24,7 +24,7 @@ namespace capy { struct env_test_coro { - struct promise_type : io_awaitable_promise_base + struct promise_type : context_awaitable_promise_base { env_test_coro get_return_object() { @@ -68,7 +68,7 @@ struct this_coro_environment_access_test { auto c = []() -> env_test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto awaiter = c.h_.promise().await_transform(this_coro::environment); @@ -76,7 +76,7 @@ struct this_coro_environment_access_test BOOST_TEST(awaiter.await_ready()); auto retrieved = awaiter.await_resume(); - static_assert(std::is_same_v); + static_assert(std::is_same_v); (void)retrieved; } @@ -86,7 +86,7 @@ struct this_coro_environment_access_test auto c = []() -> env_test_coro { co_return; }(); std::stop_source source; - io_env env; + await_context env; env.stop_token = source.get_token(); c.h_.promise().set_environment(&env); @@ -105,7 +105,7 @@ struct this_coro_environment_access_test testEnvironmentAwaiterNeverSuspends() { auto c = []() -> env_test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto awaiter = c.h_.promise().await_transform(this_coro::environment); @@ -119,7 +119,7 @@ struct this_coro_environment_access_test auto c = []() -> env_test_coro { co_return; }(); std::stop_source source; - io_env env; + await_context env; env.stop_token = source.get_token(); c.h_.promise().set_environment(&env); @@ -139,7 +139,7 @@ struct this_coro_environment_access_test { auto c = []() -> env_test_coro { co_return; }(); - io_env env; + await_context env; c.h_.promise().set_environment(&env); auto awaiter = c.h_.promise().await_transform(this_coro::executor); @@ -155,7 +155,7 @@ struct this_coro_environment_access_test auto c = []() -> env_test_coro { co_return; }(); auto* mr = std::pmr::new_delete_resource(); - io_env env; + await_context env; env.frame_allocator = mr; c.h_.promise().set_environment(&env); diff --git a/test/unit/ex/immediate.cpp b/test/unit/ex/immediate.cpp index da9488ee5..a5c398831 100644 --- a/test/unit/ex/immediate.cpp +++ b/test/unit/ex/immediate.cpp @@ -10,9 +10,10 @@ // Test that header file is self-contained. #include -#include -#include -#include +#include +#include +#include +#include #include #include "test/unit/test_helpers.hpp" @@ -22,9 +23,9 @@ namespace boost { namespace capy { -static_assert(IoAwaitable>); -static_assert(IoAwaitable>>); -static_assert(IoAwaitable>>); +static_assert(ContextAwaitable>); +static_assert(ContextAwaitable>>); +static_assert(ContextAwaitable>>); struct immediate_test { @@ -37,15 +38,15 @@ struct immediate_test BOOST_TEST(im.await_ready()); } - // immediate> is always ready + // immediate> is always ready { - immediate> im{{}}; + immediate> im{{}}; BOOST_TEST(im.await_ready()); } - // immediate> is always ready + // immediate> is always ready { - immediate> im{{std::error_code(), 100}}; + immediate> im{{std::error_code(), 100}}; BOOST_TEST(im.await_ready()); } } @@ -65,16 +66,16 @@ struct immediate_test BOOST_TEST_EQ(im.await_resume(), "hello"); } - // immediate> returns result + // immediate> returns result { - immediate> im{{}}; + immediate> im{{}}; auto r = im.await_resume(); BOOST_TEST(!std::get<0>(r)); } - // immediate> returns result with value + // immediate> returns result with value { - immediate> im{{std::error_code(), 42}}; + immediate> im{{std::error_code(), 42}}; auto r = im.await_resume(); BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r), 42u); @@ -94,13 +95,13 @@ struct immediate_test BOOST_TEST_EQ(result, 42); } - // co_await immediate> + // co_await immediate> { - auto coro = []() -> io_task { - co_return co_await immediate>{{std::error_code(), 100}}; + auto coro = []() -> task> { + co_return co_await immediate>{{std::error_code(), 100}}; }; - io_result result{}; - test::run_blocking([&](io_result v) { result = v; })(coro()); + results result{}; + test::run_blocking([&](results v) { result = v; })(coro()); BOOST_TEST(!std::get<0>(result)); BOOST_TEST_EQ(std::get<1>(result), 100u); } @@ -108,7 +109,7 @@ struct immediate_test // Structured binding with co_await { auto coro = []() -> task { - auto [ec, n] = co_await immediate>{{std::error_code(), 50}}; + auto [ec, n] = co_await immediate>{{std::error_code(), 50}}; if(ec) co_return 0; co_return n; @@ -300,7 +301,7 @@ struct immediate_test { mock_sync_parser& pr_; - immediate> + immediate> write(std::size_t n) { auto ec = pr_.write(n); @@ -309,7 +310,7 @@ struct immediate_test return ready(n); } - immediate> + immediate> write_eof() { pr_.finish(); @@ -350,9 +351,9 @@ struct immediate_test } void - testIoAwaitableProtocol() + testContextAwaitableProtocol() { - // Test await_suspend with executor (IoAwaitable protocol) + // Test await_suspend with executor (ContextAwaitable protocol) { int dispatch_count = 0; test_executor ex(dispatch_count); @@ -362,7 +363,7 @@ struct immediate_test // Even though we call await_suspend, it returns noop // because the result is already ready - io_env env{ex, {}}; + await_context env{ex, {}}; auto result = im.await_suspend( std::noop_coroutine(), &env); @@ -406,7 +407,7 @@ struct immediate_test testReadyThreeValues(); testReadyWithError(); testWriteSinkPattern(); - testIoAwaitableProtocol(); + testContextAwaitableProtocol(); testMoveSemantics(); } }; diff --git a/test/unit/ex/run.cpp b/test/unit/ex/run.cpp index cbd3fe5c4..d37a796ea 100644 --- a/test/unit/ex/run.cpp +++ b/test/unit/ex/run.cpp @@ -10,7 +10,7 @@ // Test that header file is self-contained. #include -#include +#include #include #include #include @@ -36,14 +36,14 @@ namespace boost { namespace capy { -static_assert(IoAwaitable, executor_ref, true>>); -static_assert(IoAwaitable, executor_ref, true>>); -static_assert(IoAwaitable, true>>); -static_assert(IoAwaitable, true>>); +static_assert(ContextAwaitable, executor_ref, true>>); +static_assert(ContextAwaitable, executor_ref, true>>); +static_assert(ContextAwaitable, true>>); +static_assert(ContextAwaitable, true>>); using test::custom_task; -// Destroys its frame even while lent, as a non-conforming IoRunnable +// Destroys its frame even while lent, as a non-conforming ContextRunnable // may. struct lax_task : custom_task { @@ -64,7 +64,7 @@ struct lax_task : custom_task } }; -static_assert(IoRunnable); +static_assert(ContextRunnable); // custom_task passes no environment to what it awaits. struct park_plain @@ -137,7 +137,7 @@ struct run_test void testCustomTaskType() { - // Proves run works with any IoRunnable, not just capy::task + // Proves run works with any ContextRunnable, not just capy::task int dispatch_count = 0; test_executor ex(1, dispatch_count); int result = 0; @@ -1057,7 +1057,7 @@ struct run_test int work = 0, child_down = 0; bool caught = false; counting_queue_executor caller{&q, &work}; - io_env env{executor_ref(caller), {}, nullptr}; + await_context env{executor_ref(caller), {}, nullptr}; auto child = [](destroy_count_t) -> task { co_return; }; { auto aw = capy::run(throwing_post_executor{})( @@ -1087,7 +1087,7 @@ struct run_test { int n = 0; test_executor caller(n); - io_env env{executor_ref(caller), {}, nullptr}; + await_context env{executor_ref(caller), {}, nullptr}; auto t = parent(); t.handle().promise().set_environment(&env); t.handle().resume(); diff --git a/test/unit/ex/run_async.cpp b/test/unit/ex/run_async.cpp index 44f94c6da..b833d1918 100644 --- a/test/unit/ex/run_async.cpp +++ b/test/unit/ex/run_async.cpp @@ -824,7 +824,7 @@ struct run_async_test } //---------------------------------------------------------- - // Custom Task Type (proves IoRunnable genericity) + // Custom Task Type (proves ContextRunnable genericity) //---------------------------------------------------------- static custom_task @@ -842,7 +842,7 @@ struct run_async_test void testCustomTaskType() { - // Proves run_async works with any IoRunnable, not just capy::task + // Proves run_async works with any ContextRunnable, not just capy::task int dispatch_count = 0; sync_executor d(dispatch_count); int result = 0; diff --git a/test/unit/ex/safe_resume.cpp b/test/unit/ex/safe_resume.cpp index b6fc36f68..6d6011396 100644 --- a/test/unit/ex/safe_resume.cpp +++ b/test/unit/ex/safe_resume.cpp @@ -25,7 +25,7 @@ safe_resume Tests Verifies that safe_resume saves and restores the thread-local frame allocator around h.resume(). A plain coroutine (no -io_awaitable_promise_base) is used so that operator new does +context_awaitable_promise_base) is used so that operator new does not read TLS — we only care about the TLS value itself. */ diff --git a/test/unit/ex/thread_pool.cpp b/test/unit/ex/thread_pool.cpp index a71308f61..1a9b4659e 100644 --- a/test/unit/ex/thread_pool.cpp +++ b/test/unit/ex/thread_pool.cpp @@ -261,7 +261,7 @@ struct signal_park bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { slot->h = h; slot->owner = env->owner; diff --git a/test/unit/io/any_read_stream.cpp b/test/unit/io/any_read_stream.cpp index 7d4772516..0aad1557e 100644 --- a/test/unit/io/any_read_stream.cpp +++ b/test/unit/io/any_read_stream.cpp @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -48,9 +48,9 @@ struct pending_read_awaitable : counter_(std::exchange(o.counter_, nullptr)) {} ~pending_read_awaitable() { if(counter_) ++(*counter_); } bool await_ready() const noexcept { return false; } - std::coroutine_handle<> await_suspend(std::coroutine_handle<>, io_env const*) + std::coroutine_handle<> await_suspend(std::coroutine_handle<>, await_context const*) { return std::noop_coroutine(); } - io_result await_resume() + results await_resume() { return {std::error_code(), 0}; } }; @@ -66,11 +66,11 @@ struct parking_read_awaitable ~parking_read_awaitable() { if(down_) ++(*down_); } bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { return park_awaitable{slot_}.await_suspend(h, env); } - io_result await_resume() + results await_resume() { return {std::error_code(), 0}; } }; @@ -91,18 +91,18 @@ struct pending_read_stream { return pending_read_awaitable{counter_}; } }; -// Reports readiness without consulting the io_env, like a stream +// Reports readiness without consulting the await_context, like a stream // whose data is already in user-space memory. struct ready_read_awaitable { int* suspended_; bool await_ready() const noexcept { return true; } - std::coroutine_handle<> await_suspend(std::coroutine_handle<>, io_env const*) + std::coroutine_handle<> await_suspend(std::coroutine_handle<>, await_context const*) { ++(*suspended_); return std::noop_coroutine(); } - io_result await_resume() + results await_resume() { return {std::error_code(), 7}; } }; @@ -121,9 +121,9 @@ struct resuming_read_awaitable { bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const*) noexcept + await_suspend(std::coroutine_handle<> h, await_context const*) noexcept { return h; } - io_result await_resume() { return {std::error_code(), 7}; } + results await_resume() { return {std::error_code(), 7}; } }; struct resuming_read_stream diff --git a/test/unit/io/any_stream.cpp b/test/unit/io/any_stream.cpp index 6974ba29f..f64666f22 100644 --- a/test/unit/io/any_stream.cpp +++ b/test/unit/io/any_stream.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -75,11 +75,11 @@ class mock_stream void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } - io_result + results await_resume() { auto ec = self_->f_.maybe_fail(); @@ -114,11 +114,11 @@ class mock_stream void await_suspend( std::coroutine_handle<>, - io_env const*) const noexcept + await_context const*) const noexcept { } - io_result + results await_resume() { auto ec = self_->f_.maybe_fail(); diff --git a/test/unit/io/any_write_stream.cpp b/test/unit/io/any_write_stream.cpp index d07dd7e48..323e33a3f 100644 --- a/test/unit/io/any_write_stream.cpp +++ b/test/unit/io/any_write_stream.cpp @@ -14,8 +14,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -46,9 +46,9 @@ struct pending_write_awaitable : counter_(std::exchange(o.counter_, nullptr)) {} ~pending_write_awaitable() { if(counter_) ++(*counter_); } bool await_ready() const noexcept { return false; } - std::coroutine_handle<> await_suspend(std::coroutine_handle<>, io_env const*) + std::coroutine_handle<> await_suspend(std::coroutine_handle<>, await_context const*) { return std::noop_coroutine(); } - io_result await_resume() + results await_resume() { return {std::error_code(), 0}; } }; @@ -64,11 +64,11 @@ struct parking_write_awaitable ~parking_write_awaitable() { if(down_) ++(*down_); } bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { return park_awaitable{slot_}.await_suspend(h, env); } - io_result await_resume() + results await_resume() { return {std::error_code(), 0}; } }; @@ -89,18 +89,18 @@ struct pending_write_stream { return pending_write_awaitable{counter_}; } }; -// Reports readiness without consulting the io_env, like a stream +// Reports readiness without consulting the await_context, like a stream // whose data is already in user-space memory. struct ready_write_awaitable { int* suspended_; bool await_ready() const noexcept { return true; } - std::coroutine_handle<> await_suspend(std::coroutine_handle<>, io_env const*) + std::coroutine_handle<> await_suspend(std::coroutine_handle<>, await_context const*) { ++(*suspended_); return std::noop_coroutine(); } - io_result await_resume() + results await_resume() { return {std::error_code(), 7}; } }; @@ -526,7 +526,7 @@ class any_write_stream_test test::blocking_context bctx; auto ex = bctx.get_executor(); - io_env env{executor_ref(ex), {}}; + await_context env{executor_ref(ex), {}}; aw.await_suspend(std::noop_coroutine(), &env); } BOOST_TEST_EQ(destroyed, 1); @@ -565,7 +565,7 @@ class any_write_stream_test test::blocking_context bctx; auto ex = bctx.get_executor(); - io_env env{executor_ref(ex), {}}; + await_context env{executor_ref(ex), {}}; aw.await_suspend(std::noop_coroutine(), &env); any_write_stream empty; diff --git a/test/unit/io/owned_parking_stream.hpp b/test/unit/io/owned_parking_stream.hpp index be750967e..52ad329c8 100644 --- a/test/unit/io/owned_parking_stream.hpp +++ b/test/unit/io/owned_parking_stream.hpp @@ -11,7 +11,7 @@ #define BOOST_CAPY_TEST_OWNED_PARKING_STREAM_HPP #include -#include +#include #include "test/unit/test_helpers.hpp" @@ -67,12 +67,12 @@ struct owned_parking_stream bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { return park_awaitable{&st_->slot}.await_suspend(h, env); } - io_result await_resume() noexcept + results await_resume() noexcept { return {std::error_code(), 0}; } diff --git a/test/unit/io/write_now.cpp b/test/unit/io/write_now.cpp index 08be5ce2c..0cd52ae65 100644 --- a/test/unit/io/write_now.cpp +++ b/test/unit/io/write_now.cpp @@ -13,8 +13,8 @@ #include #include #include +#include #include -#include #include #include #include @@ -40,9 +40,9 @@ struct suspending_write_awaitable { bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const*) noexcept + await_suspend(std::coroutine_handle<> h, await_context const*) noexcept { return h; } - io_result await_resume() { return {std::error_code(), 5}; } + results await_resume() { return {std::error_code(), 5}; } }; struct suspending_write_stream @@ -61,13 +61,13 @@ struct parking_stream parked* slot; bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { slot->h = h; slot->owner = env->owner; return std::noop_coroutine(); } - io_result await_resume() const noexcept + results await_resume() const noexcept { return {std::error_code(), 1}; } @@ -394,7 +394,7 @@ class write_now_test }; int n = 0; test_executor caller(n); - io_env env{executor_ref(caller), {}, nullptr}; + await_context env{executor_ref(caller), {}, nullptr}; // Started by hand, not lent, so this destroys it from above. auto t = parent(); t.handle().promise().set_environment(&env); diff --git a/test/unit/quitter.cpp b/test/unit/quitter.cpp index 183237f1d..8c83c76b2 100644 --- a/test/unit/quitter.cpp +++ b/test/unit/quitter.cpp @@ -14,11 +14,11 @@ #include #include #include -#include +#include #include #include #include -#include +#include #include #include #include @@ -43,10 +43,10 @@ namespace boost { namespace capy { -static_assert(IoAwaitable>); -static_assert(IoAwaitable>); -static_assert(IoRunnable>); -static_assert(IoRunnable>); +static_assert(ContextAwaitable>); +static_assert(ContextAwaitable>); +static_assert(ContextRunnable>); +static_assert(ContextRunnable>); struct quitter_test { @@ -72,7 +72,7 @@ struct quitter_test parked slot; int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto t = [](parked* s) -> quitter { co_await park_awaitable{s}; }(&slot); @@ -87,7 +87,7 @@ struct quitter_test parked slot; int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto body = [](parked* s) -> quitter { co_await park_awaitable{s}; }; @@ -105,7 +105,7 @@ struct quitter_test { auto t = body(); } // never started int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto t = body(); lend(t, env); // runs to final_suspend return t.handle().done(); @@ -531,7 +531,7 @@ struct quitter_test { int dispatch_count = 0; test_executor ex(dispatch_count); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; // move constructor { @@ -606,12 +606,12 @@ struct quitter_test test_exception); } - // An IoAwaitable whose await_suspend returns bool, to drive the + // A ContextAwaitable whose await_suspend returns bool, to drive the // non-handle branch of the quitter transform_awaiter. struct bool_resume_awaitable { bool await_ready() const noexcept { return false; } - bool await_suspend(std::coroutine_handle<>, io_env const*) noexcept + bool await_suspend(std::coroutine_handle<>, await_context const*) noexcept { return false; // do not suspend } @@ -677,7 +677,7 @@ struct quitter_test //---------------------------------------------------------- // 7. Stop propagation with when_all // - // Two quitter> children inside when_all. + // Two quitter> children inside when_all. // Both block on stop_only_awaitable. when_all creates a // child stop_source; when the parent stop fires, when_all // propagates it. Each quitter child intercepts the stop @@ -686,14 +686,14 @@ struct quitter_test // co_return. Verify both stop and when_all completes. //---------------------------------------------------------- - static quitter> + static quitter> quitter_pending_size(bool& reached_co_return) { co_await stop_only_awaitable{}; // If quitter's transform_awaiter intercepted the stop, // we never reach here. reached_co_return = true; - co_return io_result{ + co_return results{ make_error_code(error::canceled), 0}; } @@ -742,10 +742,10 @@ struct quitter_test // intercepts the stop and exits cleanly. //---------------------------------------------------------- - static quitter> + static quitter> quitter_success_size(std::size_t n) { - co_return io_result{std::error_code(), n}; + co_return results{std::error_code(), n}; } void diff --git a/test/unit/read.cpp b/test/unit/read.cpp index cc5bd1f37..0e9139081 100644 --- a/test/unit/read.cpp +++ b/test/unit/read.cpp @@ -148,9 +148,9 @@ struct contingent_read_stream bool await_ready() const noexcept { return true; } void await_suspend( - std::coroutine_handle<>, io_env const*) const noexcept {} + std::coroutine_handle<>, await_context const*) const noexcept {} - io_result + results await_resume() { std::size_t const cap = buffer_size(buffers_); diff --git a/test/unit/read_at_least.cpp b/test/unit/read_at_least.cpp index 926dc4bb7..fc81ce617 100644 --- a/test/unit/read_at_least.cpp +++ b/test/unit/read_at_least.cpp @@ -75,9 +75,9 @@ struct contingent_read_stream bool await_ready() const noexcept { return true; } void await_suspend( - std::coroutine_handle<>, io_env const*) const noexcept {} + std::coroutine_handle<>, await_context const*) const noexcept {} - io_result + results await_resume() { std::size_t const cap = buffer_size(buffers_); diff --git a/test/unit/io_result.cpp b/test/unit/results.cpp similarity index 78% rename from test/unit/io_result.cpp rename to test/unit/results.cpp index 50b627325..b966b1b0b 100644 --- a/test/unit/io_result.cpp +++ b/test/unit/results.cpp @@ -8,7 +8,7 @@ // // Test that header file is self-contained. -#include +#include #include #include @@ -18,17 +18,17 @@ namespace boost { namespace capy { -struct io_result_test +struct results_test { void testVoidResult() { // Default construction - io_result<> r1; + results<> r1; BOOST_TEST(!std::get<0>(r1)); // With error - io_result<> r2{make_error_code(std::errc::invalid_argument)}; + results<> r2{make_error_code(std::errc::invalid_argument)}; BOOST_TEST(std::get<0>(r2)); // Structured binding @@ -40,17 +40,17 @@ struct io_result_test testSizeResult() { // Default construction - io_result r1; + results r1; BOOST_TEST(!std::get<0>(r1)); BOOST_TEST_EQ(std::get<1>(r1), 0u); // With values - io_result r2{std::error_code(), 42}; + results r2{std::error_code(), 42}; BOOST_TEST(!std::get<0>(r2)); BOOST_TEST_EQ(std::get<1>(r2), 42u); // With error - io_result r3{ + results r3{ make_error_code(std::errc::invalid_argument), 10}; BOOST_TEST(std::get<0>(r3)); BOOST_TEST_EQ(std::get<1>(r3), 10u); @@ -65,7 +65,7 @@ struct io_result_test testGenericSingleValue() { // With string value - io_result r1{std::error_code(), "hello"}; + results r1{std::error_code(), "hello"}; BOOST_TEST(!std::get<0>(r1)); BOOST_TEST_EQ(std::get<1>(r1), "hello"); @@ -75,7 +75,7 @@ struct io_result_test BOOST_TEST_EQ(v, "hello"); // With error - io_result r2{ + results r2{ make_error_code(std::errc::invalid_argument), "error"}; BOOST_TEST(std::get<0>(r2)); BOOST_TEST_EQ(std::get<1>(r2), "error"); @@ -85,7 +85,7 @@ struct io_result_test testMultiValue() { // With multiple values - io_result r1{ + results r1{ {}, 42, 3.14, std::string("test")}; BOOST_TEST(!std::get<0>(r1)); BOOST_TEST_EQ(std::get<1>(r1), 42); @@ -100,7 +100,7 @@ struct io_result_test BOOST_TEST_EQ(c, "test"); // With error - io_result r2{ + results r2{ make_error_code(std::errc::invalid_argument), 0, 0.0}; BOOST_TEST(std::get<0>(r2)); BOOST_TEST_EQ(std::get<1>(r2), 0); @@ -111,7 +111,7 @@ struct io_result_test testFourPlusArgs() { // Verify no arity limit - io_result r1{ + results r1{ {}, 1, 2.5, std::string("hi"), true}; BOOST_TEST(!std::get<0>(r1)); BOOST_TEST_EQ(std::get<1>(r1), 1); @@ -128,7 +128,7 @@ struct io_result_test BOOST_TEST_EQ(d, true); // Default construction - io_result r2; + results r2; BOOST_TEST(!std::get<0>(r2)); BOOST_TEST_EQ(std::get<1>(r2), 0); BOOST_TEST_EQ(std::get<4>(r2), false); @@ -141,18 +141,18 @@ struct io_result_test // avoids structured bindings entirely std::error_code ec; std::size_t n = 0; - std::tie(ec, n) = io_result{std::error_code(), 42}; + std::tie(ec, n) = results{std::error_code(), 42}; BOOST_TEST(!ec); BOOST_TEST_EQ(n, 42u); - std::tie(ec, n) = io_result{ + std::tie(ec, n) = results{ make_error_code(std::errc::invalid_argument), 7}; BOOST_TEST(ec); BOOST_TEST_EQ(n, 7u); // Partial rebinding with std::ignore std::tie(ec, std::ignore) = - io_result{std::error_code(), 99}; + results{std::error_code(), 99}; BOOST_TEST(!ec); BOOST_TEST_EQ(n, 7u); } @@ -160,7 +160,7 @@ struct io_result_test void testApply() { - io_result r{std::error_code(), 42}; + results r{std::error_code(), 42}; auto sum = std::apply( [](std::error_code ec, std::size_t n) { @@ -173,8 +173,8 @@ struct io_result_test testTupleInterop() { // Comparison - io_result r1{std::error_code(), 42}; - io_result r2{std::error_code(), 42}; + results r1{std::error_code(), 42}; + results r2{std::error_code(), 42}; BOOST_TEST(r1 == r2); // Assignment from a plain tuple @@ -194,15 +194,15 @@ struct io_result_test { // Outcome detection is structural: any tuple with a // leading error_code qualifies, no others do - static_assert(detail::is_io_result_v>); - static_assert(detail::is_io_result_v< - io_result>); - static_assert(detail::is_io_result_v< + static_assert(detail::is_results_v>); + static_assert(detail::is_results_v< + results>); + static_assert(detail::is_results_v< std::tuple>); - static_assert(!detail::is_io_result_v< + static_assert(!detail::is_results_v< std::tuple>); - static_assert(!detail::is_io_result_v>); - static_assert(!detail::is_io_result_v); + static_assert(!detail::is_results_v>); + static_assert(!detail::is_results_v); } void @@ -220,7 +220,7 @@ struct io_result_test } }; -TEST_SUITE(io_result_test, "boost.capy.io_result"); +TEST_SUITE(results_test, "boost.capy.results"); } // namespace capy } // namespace boost diff --git a/test/unit/task.cpp b/test/unit/task.cpp index a5fad28d7..7c9cdecca 100644 --- a/test/unit/task.cpp +++ b/test/unit/task.cpp @@ -11,7 +11,7 @@ #include #include -#include +#include #include #include #include @@ -37,19 +37,19 @@ namespace boost { namespace capy { -static_assert(IoAwaitable>); -static_assert(IoAwaitable>); -static_assert(IoRunnable>); -static_assert(IoRunnable>); +static_assert(ContextAwaitable>); +static_assert(ContextAwaitable>); +static_assert(ContextRunnable>); +static_assert(ContextRunnable>); -// Verify set_continuation/set_environment are part of IoRunnable +// Verify set_continuation/set_environment are part of ContextRunnable static_assert(requires(task::promise_type& p) { { p.set_continuation(std::coroutine_handle<>{}) } noexcept; - { p.set_environment(static_cast(nullptr)) } noexcept; + { p.set_environment(static_cast(nullptr)) } noexcept; }); static_assert(requires(task::promise_type& p) { { p.set_continuation(std::coroutine_handle<>{}) } noexcept; - { p.set_environment(static_cast(nullptr)) } noexcept; + { p.set_environment(static_cast(nullptr)) } noexcept; }); /** Tracking executor that logs dispatch calls with an ID. @@ -137,7 +137,7 @@ struct task_test parked slot; int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto t = [](parked* s) -> task { co_await park_awaitable{s}; }(&slot); @@ -152,7 +152,7 @@ struct task_test parked slot; int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto body = [](parked* s) -> task { co_await park_awaitable{s}; }; @@ -170,7 +170,7 @@ struct task_test { auto t = body(); } // never started int n = 0; test_executor ex(n); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto t = body(); lend(t, env); // runs to final_suspend return t.handle().done(); @@ -351,7 +351,7 @@ struct task_test { int dispatch_count = 0; test_executor ex(dispatch_count); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; // Value task: exception stored but never observed { @@ -467,7 +467,7 @@ struct task_test { int dispatch_count = 0; test_executor ex(dispatch_count); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; // move constructor { @@ -596,7 +596,7 @@ struct task_test { int dispatch_count = 0; test_executor ex(dispatch_count); - io_env env{executor_ref(ex), {}, nullptr}; + await_context env{executor_ref(ex), {}, nullptr}; auto t1 = void_task_basic(); auto h = t1.handle(); @@ -1619,12 +1619,12 @@ struct task_test BOOST_TEST(state == 2); } - // An IoAwaitable whose await_suspend returns bool, to drive the + // A ContextAwaitable whose await_suspend returns bool, to drive the // non-handle branch of the task transform_awaiter. struct bool_resume_awaitable { bool await_ready() const noexcept { return false; } - bool await_suspend(std::coroutine_handle<>, io_env const*) noexcept + bool await_suspend(std::coroutine_handle<>, await_context const*) noexcept { return false; // do not suspend } diff --git a/test/unit/test/stream.cpp b/test/unit/test/stream.cpp index a80285237..93aa6109d 100644 --- a/test/unit/test/stream.cpp +++ b/test/unit/test/stream.cpp @@ -16,11 +16,11 @@ #include #include #include +#include #include -#include #include #include -#include +#include #include #include #include @@ -703,7 +703,7 @@ class stream_test auto h = reader.handle(); reader.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -775,7 +775,7 @@ class stream_test auto h = reader.handle(); reader.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -809,7 +809,7 @@ class stream_test auto h = reader.handle(); reader.release(); - io_env env; + await_context env; env.executor = executor_ref(ex); env.stop_token = ss.get_token(); h.promise().set_environment(&env); @@ -1037,25 +1037,25 @@ class stream_pair_test auto [a, b] = make_stream_pair(f); (void) co_await when_all( - [](stream a) -> io_task<> { + [](stream a) -> task> { char buf[32] = {}; auto [ec, n] = co_await a.read_some( make_buffer(buf)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 5u); BOOST_TEST_EQ( std::string_view(buf, n), "hello"); - co_return io_result<>{}; + co_return results<>{}; }(std::move(a)), - [](stream b) -> io_task<> { + [](stream b) -> task> { auto [ec, n] = co_await b.write_some( make_buffer("hello", 5)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 5u); - co_return io_result<>{}; + co_return results<>{}; }(std::move(b)) ); }); @@ -1256,25 +1256,25 @@ class stream_pair_test auto [a, b] = make_stream_pair(f); (void) co_await when_all( - [](stream a) -> io_task<> { + [](stream a) -> task> { char buf[3] = {}; auto [ec, n] = co_await a.read_some( make_buffer(buf)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 3u); BOOST_TEST_EQ( std::string_view(buf, n), "hel"); - co_return io_result<>{}; + co_return results<>{}; }(std::move(a)), - [](stream b) -> io_task<> { + [](stream b) -> task> { auto [ec, n] = co_await b.write_some( make_buffer("hello", 5)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 5u); - co_return io_result<>{}; + co_return results<>{}; }(std::move(b)) ); }); @@ -1347,17 +1347,17 @@ class stream_pair_test auto [a, b] = make_stream_pair(f); (void) co_await when_all( - [](stream a) -> io_task<> { + [](stream a) -> task> { char buf[32] = {}; auto [ec, n] = co_await a.read_some( make_buffer(buf)); BOOST_TEST(ec == cond::eof); BOOST_TEST_EQ(n, 0u); - co_return io_result<>{ec}; + co_return results<>{ec}; }(std::move(a)), - [](stream b) -> io_task<> { + [](stream b) -> task> { b.close(); - co_return io_result<>{}; + co_return results<>{}; }(std::move(b)) ); }()); @@ -1405,7 +1405,7 @@ class stream_pair_test auto [a, b] = make_stream_pair(f); (void) co_await when_all( - [](stream a) -> io_task<> { + [](stream a) -> task> { // Reader suspends waiting for data. // Gets data, eof from peer's guard, // or its own fuse error on resume. @@ -1413,19 +1413,19 @@ class stream_pair_test auto [ec, n] = co_await a.read_some( make_buffer(buf)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 5u); - co_return io_result<>{}; + co_return results<>{}; }(std::move(a)), - [](stream b) -> io_task<> { + [](stream b) -> task> { // Writer may get fuse error, which // closes the peer via the guard auto [ec, n] = co_await b.write_some( make_buffer("hello", 5)); if(ec) - co_return io_result<>{ec}; + co_return results<>{ec}; BOOST_TEST_EQ(n, 5u); - co_return io_result<>{}; + co_return results<>{}; }(std::move(b)) ); }); diff --git a/test/unit/test_helpers.hpp b/test/unit/test_helpers.hpp index 8be99caae..c0369430d 100644 --- a/test/unit/test_helpers.hpp +++ b/test/unit/test_helpers.hpp @@ -15,9 +15,9 @@ #include #include #include +#include #include #include -#include #include #include @@ -267,7 +267,7 @@ struct self_destroy_awaitable { bool await_ready() {return false;} - std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { continuation c{h, nullptr, env->owner}; capy::abandon(c); @@ -296,7 +296,7 @@ using stop_resume_callback = std::stop_callback; inline resume_via_post post_resume( - io_env const& env, + await_context const& env, std::coroutine_handle<> h) noexcept { return resume_via_post{env.executor, continuation{h, nullptr, env.owner}}; @@ -334,7 +334,7 @@ struct stop_only_awaitable bool await_ready() {return false;} - std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { if (env->stop_token.stop_requested()) return h; @@ -600,7 +600,7 @@ struct yield_awaitable return false; } - std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, io_env const* env) + std::coroutine_handle<> await_suspend(std::coroutine_handle<> h, await_context const* env) { // Post ourselves back to the queue cont_.h = h; @@ -661,7 +661,7 @@ struct park_awaitable bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const* env) noexcept + await_suspend(std::coroutine_handle<> h, await_context const* env) noexcept { slot->h = h; slot->owner = env->owner; diff --git a/test/unit/when_all.cpp b/test/unit/when_all.cpp index b99ff3fc7..a115f823d 100644 --- a/test/unit/when_all.cpp +++ b/test/unit/when_all.cpp @@ -19,7 +19,7 @@ #include #include #include -#include +#include #include #include "test_helpers.hpp" @@ -46,7 +46,7 @@ namespace boost { namespace capy { // Verify when_all returns task which satisfies awaitable protocols -static_assert(IoAwaitable>>); +static_assert(ContextAwaitable>>); struct when_all_strand_test { @@ -62,10 +62,10 @@ struct when_all_strand_test std::latch done(1); bool completed = false; - auto outer = [&]() -> task, std::tuple<>>> { + auto outer = [&]() -> task, std::tuple<>>> { co_return co_await when_all( - []() -> io_task<> { co_return io_result<>{{}}; }(), - []() -> io_task<> { co_return io_result<>{{}}; }() + []() -> task> { co_return results<>{{}}; }(), + []() -> task> { co_return results<>{{}}; }() ); }; @@ -93,18 +93,18 @@ struct when_all_strand_test bool completed = false; size_t result = 0; - auto outer = [&]() -> task> { + auto outer = [&]() -> task> { co_return co_await when_all( - []() -> io_task { - co_return io_result{std::error_code(), 10}; + []() -> task> { + co_return results{std::error_code(), 10}; }(), - []() -> io_task { - co_return io_result{std::error_code(), 20}; + []() -> task> { + co_return results{std::error_code(), 20}; }()); }; run_async(s, - [&](io_result r) { + [&](results r) { completed = true; result = std::get<1>(r) + std::get<2>(r); done.count_down(); @@ -131,35 +131,35 @@ TEST_SUITE( when_all_strand_test, "boost.capy.when_all_strand"); -// Verify IoAwaitableRange concept -static_assert(IoAwaitableRange>>); -static_assert(IoAwaitableRange>>); +// Verify ContextAwaitableRange concept +static_assert(ContextAwaitableRange>>>); +static_assert(ContextAwaitableRange>>>); -// io_task helpers for io_result-aware spec tests +// task> helpers for results-aware spec tests namespace { -io_task -io_success_size(size_t n) +task> +results_success_size(size_t n) { - co_return io_result{std::error_code(), n}; + co_return results{std::error_code(), n}; } -io_task -io_error_size(std::error_code ec, size_t n = 0) +task> +results_error_size(std::error_code ec, size_t n = 0) { - co_return io_result{ec, n}; + co_return results{ec, n}; } -io_task<> -io_void_ok() +task> +results_void_ok() { - co_return io_result<>{}; + co_return results<>{}; } -io_task<> -io_void_error(std::error_code ec) +task> +results_void_error(std::error_code ec) { - co_return io_result<>{ec}; + co_return results<>{ec}; } #if defined(_MSC_VER) @@ -167,42 +167,42 @@ io_void_error(std::error_code ec) #pragma warning(disable : 4702) // unreachable code after throw #endif -io_task<> -io_void_throws(char const* msg) +task> +results_void_throws(char const* msg) { throw test_exception(msg); - co_return io_result<>{}; + co_return results<>{}; } -io_task -io_throws_size(char const* msg) +task> +results_throws_size(char const* msg) { throw test_exception(msg); - co_return io_result{std::error_code(), 0}; + co_return results{std::error_code(), 0}; } #if defined(_MSC_VER) #pragma warning(pop) #endif -io_task -io_success_string(std::string s) +task> +results_success_string(std::string s) { - co_return io_result{std::error_code(), std::move(s)}; + co_return results{std::error_code(), std::move(s)}; } -io_task -io_success_size_int(size_t n, int flags) +task> +results_success_size_int(size_t n, int flags) { - co_return io_result{std::error_code(), n, flags}; + co_return results{std::error_code(), n, flags}; } // Suspends until stop token fires, then returns ECANCELED. -io_task -io_pending_size() +task> +results_pending_size() { co_await stop_only_awaitable{}; - co_return io_result{make_error_code(error::canceled), 0}; + co_return results{make_error_code(error::canceled), 0}; } } // anonymous namespace @@ -216,11 +216,11 @@ struct when_all_range_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_size(42)); + std::vector>> tasks; + tasks.push_back(results_success_size(42)); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r).size(), 1u); @@ -239,13 +239,13 @@ struct when_all_range_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_size(10)); - tasks.push_back(io_success_size(20)); - tasks.push_back(io_success_size(30)); + std::vector>> tasks; + tasks.push_back(results_success_size(10)); + tasks.push_back(results_success_size(20)); + tasks.push_back(results_success_size(30)); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r).size(), 3u); @@ -270,12 +270,12 @@ struct when_all_range_test stashing_range tasks([](std::size_t i) { - return io_success_size((i + 1) * 10); + return results_success_size((i + 1) * 10); }, 3); static_assert(!std::ranges::forward_range); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r).size(), 3u); @@ -301,9 +301,9 @@ struct when_all_range_test int down = 0; bool completed = false; bool threw = false; - auto body = [](destroy_count_t) -> io_task + auto body = [](destroy_count_t) -> task> { - co_return io_result{std::error_code(), 1}; + co_return results{std::error_code(), 1}; }; auto tasks = std::views::iota(0, 3) | std::views::transform([&](int i) @@ -314,7 +314,7 @@ struct when_all_range_test }); run_async(ex, - [&](io_result>) { completed = true; }, + [&](results>) { completed = true; }, [&](std::exception_ptr) { threw = true; })( when_all(tasks)); @@ -333,11 +333,11 @@ struct when_all_range_test auto tasks = std::views::iota(1, 4) | std::views::transform([](int i) { - return io_success_size(static_cast(i * 10)); + return results_success_size(static_cast(i * 10)); }); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r).size(), 3u); @@ -358,10 +358,10 @@ struct when_all_range_test test_executor ex(dc); bool caught = false; - std::vector> tasks; + std::vector>> tasks; run_async(ex, - [](io_result>) {}, + [](results>) {}, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); @@ -380,13 +380,13 @@ struct when_all_range_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_ok()); + std::vector>> tasks; + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_ok()); run_async(ex, - [&](io_result<> r) { + [&](results<> r) { completed = true; BOOST_TEST(!std::get<0>(r)); }, @@ -403,10 +403,10 @@ struct when_all_range_test test_executor ex(dc); bool caught = false; - std::vector> tasks; + std::vector>> tasks; run_async(ex, - [](io_result<>) {}, + [](results<>) {}, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); @@ -426,12 +426,12 @@ struct when_all_range_test bool completed = false; std::error_code result_ec; - std::vector> tasks; - tasks.push_back(io_error_size(make_error_code(error::eof))); - tasks.push_back(io_pending_size()); + std::vector>> tasks; + tasks.push_back(results_error_size(make_error_code(error::eof))); + tasks.push_back(results_pending_size()); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; result_ec = std::get<0>(r); }, @@ -450,12 +450,12 @@ struct when_all_range_test bool completed = false; std::error_code result_ec; - std::vector> tasks; - tasks.push_back(io_error_size(make_error_code(error::eof))); - tasks.push_back(io_error_size(make_error_code(error::timeout))); + std::vector>> tasks; + tasks.push_back(results_error_size(make_error_code(error::eof))); + tasks.push_back(results_error_size(make_error_code(error::timeout))); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; result_ec = std::get<0>(r); }, @@ -474,13 +474,13 @@ struct when_all_range_test bool caught = false; std::string msg; - std::vector> tasks; - tasks.push_back(io_success_size(1)); - tasks.push_back(io_throws_size("range error")); - tasks.push_back(io_success_size(3)); + std::vector>> tasks; + tasks.push_back(results_success_size(1)); + tasks.push_back(results_throws_size("range error")); + tasks.push_back(results_success_size(3)); run_async(ex, - [](io_result>) {}, + [](results>) {}, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); @@ -502,12 +502,12 @@ struct when_all_range_test bool completed = false; std::error_code result_ec; - std::vector> tasks; - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_error(make_error_code(error::eof))); + std::vector>> tasks; + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_error(make_error_code(error::eof))); run_async(ex, - [&](io_result<> r) { + [&](results<> r) { completed = true; result_ec = std::get<0>(r); }, @@ -525,12 +525,12 @@ struct when_all_range_test test_executor ex(dc); bool caught = false; - std::vector> tasks; - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_throws("void range error")); + std::vector>> tasks; + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_throws("void range error")); run_async(ex, - [](io_result<>) {}, + [](results<>) {}, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); @@ -549,12 +549,12 @@ struct when_all_range_test test_executor ex(dc); bool caught = false; - std::vector> tasks; - tasks.push_back(io_throws_size("exception wins")); - tasks.push_back(io_error_size(make_error_code(error::eof))); + std::vector>> tasks; + tasks.push_back(results_throws_size("exception wins")); + tasks.push_back(results_error_size(make_error_code(error::eof))); run_async(ex, - [](io_result>) {}, + [](results>) {}, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); @@ -574,23 +574,23 @@ struct when_all_range_test std::atomic completion_count{0}; bool completed = false; - auto counting_io = [&]() -> io_task { + auto counting_io = [&]() -> task> { ++completion_count; - co_return io_result{std::error_code(), 1}; + co_return results{std::error_code(), 1}; }; - auto failing_io = [&]() -> io_task { + auto failing_io = [&]() -> task> { ++completion_count; - co_return io_result{make_error_code(error::eof), 0}; + co_return results{make_error_code(error::eof), 0}; }; - std::vector> tasks; + std::vector>> tasks; tasks.push_back(counting_io()); tasks.push_back(failing_io()); tasks.push_back(counting_io()); run_async(ex, - [&](io_result>) { + [&](results>) { completed = true; }, [](std::exception_ptr) {})( @@ -608,11 +608,11 @@ struct when_all_range_test bool success_called = false; bool error_called = false; - std::vector> tasks; - tasks.push_back(io_error_size(make_error_code(error::eof))); + std::vector>> tasks; + tasks.push_back(results_error_size(make_error_code(error::eof))); run_async(ex, - [&](io_result> r) { + [&](results> r) { success_called = true; BOOST_TEST(!!std::get<0>(r)); }, @@ -631,13 +631,13 @@ struct when_all_range_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_string("first")); - tasks.push_back(io_success_string("second")); - tasks.push_back(io_success_string("third")); + std::vector>> tasks; + tasks.push_back(results_success_string("first")); + tasks.push_back(results_success_string("second")); + tasks.push_back(results_success_string("third")); run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r)[0], "first"); @@ -657,20 +657,20 @@ struct when_all_range_test test_executor ex(dc); bool completed = false; - auto range_task = []() -> io_task> { - std::vector> tasks; - tasks.push_back(io_success_size(1)); - tasks.push_back(io_success_size(2)); - tasks.push_back(io_success_size(3)); + auto range_task = []() -> task>> { + std::vector>> tasks; + tasks.push_back(results_success_size(1)); + tasks.push_back(results_success_size(2)); + tasks.push_back(results_success_size(3)); co_return co_await when_all(std::move(tasks)); }; - auto io_size_task = []() -> io_task { - co_return io_result{std::error_code(), 99}; + auto results_size_task = []() -> task> { + co_return results{std::error_code(), 99}; }; run_async(ex, - [&](io_result, size_t> r) { + [&](results, size_t> r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r).size(), 3u); @@ -678,7 +678,7 @@ struct when_all_range_test BOOST_TEST_EQ(std::get<2>(r), 99u); }, [](std::exception_ptr) {})( - when_all(range_task(), io_size_task())); + when_all(range_task(), results_size_task())); BOOST_TEST(completed); } @@ -692,15 +692,15 @@ struct when_all_range_test bool completed = false; size_t result = 0; - auto outer = [&]() -> task>> { - std::vector> tasks; - tasks.push_back(io_success_size(10)); - tasks.push_back(io_success_size(20)); + auto outer = [&]() -> task>> { + std::vector>> tasks; + tasks.push_back(results_success_size(10)); + tasks.push_back(results_success_size(20)); co_return co_await when_all(std::move(tasks)); }; run_async(s, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); result = std::get<1>(r)[0] + std::get<1>(r)[1]; @@ -729,12 +729,12 @@ struct when_all_range_test src.request_stop(); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_size(1)); - tasks.push_back(io_success_size(2)); + std::vector>> tasks; + tasks.push_back(results_success_size(1)); + tasks.push_back(results_success_size(2)); run_async(ex, src.get_token(), - [&](io_result>) { completed = true; }, + [&](results>) { completed = true; }, [](std::exception_ptr) {})(when_all(std::move(tasks))); BOOST_TEST(completed); @@ -750,8 +750,8 @@ struct when_all_range_test src.request_stop(); bool completed = false; - auto outer = []() -> task, std::tuple<>>> { - co_return co_await when_all(io_void_ok(), io_void_ok()); + auto outer = []() -> task, std::tuple<>>> { + co_return co_await when_all(results_void_ok(), results_void_ok()); }; run_async(ex, src.get_token(), @@ -792,9 +792,9 @@ TEST_SUITE( when_all_range_test, "boost.capy.when_all_range"); -// Tests for io_result-aware when_all behavior per the combinators spec. +// Tests for results-aware when_all behavior per the combinators spec. // Each test is labelled with the spec row it verifies. -struct when_all_io_result_test +struct when_all_results_test { // Spec Row 1: All tasks return !ec // Return tuple of all results. No cancellation. @@ -807,7 +807,7 @@ struct when_all_io_result_test size_t n1 = 0, n2 = 0, n3 = 0; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; BOOST_TEST(!std::get<0>(r)); n1 = std::get<1>(r); @@ -816,9 +816,9 @@ struct when_all_io_result_test }, [](std::exception_ptr) {})( when_all( - io_success_size(10), - io_success_size(20), - io_success_size(30))); + results_success_size(10), + results_success_size(20), + results_success_size(30))); BOOST_TEST(completed); BOOST_TEST_EQ(n1, 10u); @@ -836,13 +836,13 @@ struct when_all_io_result_test size_t result = 0; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; BOOST_TEST(!std::get<0>(r)); result = std::get<1>(r); }, [](std::exception_ptr) {})( - when_all(io_success_size(42))); + when_all(results_success_size(42))); BOOST_TEST(completed); BOOST_TEST_EQ(result, 42u); @@ -859,14 +859,14 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof)), - io_pending_size())); + results_error_size(make_error_code(error::eof)), + results_pending_size())); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -883,14 +883,14 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof)), - io_error_size(make_error_code(error::timeout)))); + results_error_size(make_error_code(error::eof)), + results_error_size(make_error_code(error::timeout)))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -907,14 +907,14 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof), 0), - io_pending_size())); + results_error_size(make_error_code(error::eof), 0), + results_pending_size())); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -932,14 +932,14 @@ struct when_all_io_result_test size_t partial = 0; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); partial = std::get<1>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof), 42))); + results_error_size(make_error_code(error::eof), 42))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -957,15 +957,15 @@ struct when_all_io_result_test size_t n1 = 0; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); n1 = std::get<1>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof), 42), - io_pending_size())); + results_error_size(make_error_code(error::eof), 42), + results_pending_size())); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -982,13 +982,13 @@ struct when_all_io_result_test bool completed = false; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; BOOST_TEST(!std::get<0>(r)); BOOST_TEST_EQ(std::get<1>(r), 0u); }, [](std::exception_ptr) {})( - when_all(io_success_size(0))); + when_all(results_success_size(0))); BOOST_TEST(completed); } @@ -1004,13 +1004,13 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof), 0))); + results_error_size(make_error_code(error::eof), 0))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -1028,7 +1028,7 @@ struct when_all_io_result_test std::string msg; run_async(ex, - [&](io_result) { completed = true; }, + [&](results) { completed = true; }, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); } catch (test_exception const& e) { @@ -1036,8 +1036,8 @@ struct when_all_io_result_test msg = e.what(); } })(when_all( - io_throws_size("boom"), - io_pending_size())); + results_throws_size("boom"), + results_pending_size())); BOOST_TEST(!completed); BOOST_TEST(caught); @@ -1056,7 +1056,7 @@ struct when_all_io_result_test std::string msg; run_async(ex, - [&](io_result) { completed = true; }, + [&](results) { completed = true; }, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); } catch (test_exception const& e) { @@ -1064,8 +1064,8 @@ struct when_all_io_result_test msg = e.what(); } })(when_all( - io_throws_size("first"), - io_throws_size("second"))); + results_throws_size("first"), + results_throws_size("second"))); BOOST_TEST(!completed); BOOST_TEST(caught); @@ -1084,7 +1084,7 @@ struct when_all_io_result_test std::string msg; run_async(ex, - [&](io_result) { completed = true; }, + [&](results) { completed = true; }, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); } catch (test_exception const& e) { @@ -1092,8 +1092,8 @@ struct when_all_io_result_test msg = e.what(); } })(when_all( - io_throws_size("exception wins"), - io_error_size(make_error_code(error::eof)))); + results_throws_size("exception wins"), + results_error_size(make_error_code(error::eof)))); BOOST_TEST(!completed); BOOST_TEST(caught); @@ -1111,13 +1111,13 @@ struct when_all_io_result_test bool caught = false; run_async(ex, - [&](io_result) { completed = true; }, + [&](results) { completed = true; }, [&](std::exception_ptr ep) { try { std::rethrow_exception(ep); } catch (test_exception const&) { caught = true; } })(when_all( - io_error_size(make_error_code(error::eof)), - io_throws_size("exception"))); + results_error_size(make_error_code(error::eof)), + results_throws_size("exception"))); BOOST_TEST(!completed); BOOST_TEST(caught); @@ -1135,14 +1135,14 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::canceled)), - io_error_size(make_error_code(error::canceled)))); + results_error_size(make_error_code(error::canceled)), + results_error_size(make_error_code(error::canceled)))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::canceled); @@ -1159,21 +1159,21 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_error_size(make_error_code(error::eof)), - io_error_size(make_error_code(error::timeout)), - io_error_size(make_error_code(error::canceled)))); + results_error_size(make_error_code(error::eof)), + results_error_size(make_error_code(error::timeout)), + results_error_size(make_error_code(error::canceled)))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); } - // Spec Row 13: Failure reaches caller via io_result's ec + // Spec Row 13: Failure reaches caller via results's ec // Error goes through success handler, not exception handler. void testErrorViaSuccessHandler() @@ -1184,14 +1184,14 @@ struct when_all_io_result_test bool error_called = false; run_async(ex, - [&](io_result r) { + [&](results r) { success_called = true; BOOST_TEST(!!std::get<0>(r)); }, [&](std::exception_ptr) { error_called = true; })(when_all( - io_error_size(make_error_code(error::eof)))); + results_error_size(make_error_code(error::eof)))); BOOST_TEST(success_called); BOOST_TEST(!error_called); @@ -1208,7 +1208,7 @@ struct when_all_io_result_test std::string s; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; BOOST_TEST(!std::get<0>(r)); n = std::get<1>(r); @@ -1216,15 +1216,15 @@ struct when_all_io_result_test }, [](std::exception_ptr) {})( when_all( - io_success_size(42), - io_success_string("hello"))); + results_success_size(42), + results_success_string("hello"))); BOOST_TEST(completed); BOOST_TEST_EQ(n, 42u); BOOST_TEST_EQ(s, "hello"); } - // Spec Row 14 (multi-value child: io_result contributes tuple) + // Spec Row 14 (multi-value child: results contributes tuple) void testMultiValueChild() { @@ -1235,7 +1235,7 @@ struct when_all_io_result_test std::tuple tf; run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); n = std::get<1>(r); @@ -1243,8 +1243,8 @@ struct when_all_io_result_test }, [](std::exception_ptr) {})( when_all( - io_success_size(42), - io_success_size_int(10, 7))); + results_success_size(42), + results_success_size_int(10, 7))); BOOST_TEST(completed); BOOST_TEST_EQ(n, 42u); @@ -1252,7 +1252,7 @@ struct when_all_io_result_test BOOST_TEST_EQ(std::get<1>(tf), 7); } - // Spec Row 14 (void results: io_result<> contributes tuple<>) + // Spec Row 14 (void results: results<> contributes tuple<>) void testVoidResults() { @@ -1262,15 +1262,15 @@ struct when_all_io_result_test size_t n = 0; run_async(ex, - [&](io_result> r) { + [&](results> r) { completed = true; BOOST_TEST(!std::get<0>(r)); n = std::get<1>(r); }, [](std::exception_ptr) {})( when_all( - io_success_size(42), - io_void_ok())); + results_success_size(42), + results_void_ok())); BOOST_TEST(completed); BOOST_TEST_EQ(n, 42u); @@ -1288,14 +1288,14 @@ struct when_all_io_result_test std::error_code result_ec; run_async(ex, - [&](io_result r) { + [&](results r) { completed = true; result_ec = std::get<0>(r); }, [](std::exception_ptr) {})( when_all( - io_pending_size(), - io_error_size(make_error_code(error::eof)))); + results_pending_size(), + results_error_size(make_error_code(error::eof)))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -1307,9 +1307,9 @@ struct when_all_io_result_test { int a_down = 0, b_down = 0, parent_down = 0; std::queue q; - auto branch = [](destroy_count_t) -> task> + auto branch = [](destroy_count_t) -> task> { - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1347,10 +1347,10 @@ struct when_all_io_result_test int a_down = 0, b_down = 0, parent_down = 0; parked sa, sb; auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1377,10 +1377,10 @@ struct when_all_io_result_test int parent_down = 0; bool resumed = false; parked sa, sb; - auto branch = [](parked* s) -> task> + auto branch = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1398,9 +1398,9 @@ struct when_all_io_result_test // Parks the when_all runner itself, so tearing it down enters // through the runner's promise rather than through a branch task. - struct park_io_awaitable : park_awaitable + struct park_context_awaitable : park_awaitable { - io_result<> await_resume() const noexcept { return {}; } + results<> await_resume() const noexcept { return {}; } }; // Losing every branch under run_async(ex, alloc) frees the runner @@ -1414,7 +1414,7 @@ struct when_all_io_result_test parked* b, destroy_count_t) -> task { std::ignore = co_await when_all( - park_io_awaitable{{a}}, park_io_awaitable{{b}}); + park_context_awaitable{{a}}, park_context_awaitable{{b}}); }; int n = 0; test_executor ex(n); @@ -1436,10 +1436,10 @@ struct when_all_io_result_test parked sa, sb; test_executor ex(n); auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto join = [&]() -> task { @@ -1471,14 +1471,14 @@ struct when_all_io_result_test int down[count] = {}, parent_down = 0; parked slots[count]; auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { - std::vector>> tasks; + std::vector>> tasks; for(int i = 0; i < count; ++i) tasks.push_back( branch(&slots[i], destroy_count_t(down[i]))); @@ -1504,10 +1504,10 @@ struct when_all_io_result_test int parent_down = 0; bool resumed = false; parked sa, sb; - auto branch = [](parked* s) -> task> + auto branch = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1530,17 +1530,17 @@ struct when_all_io_result_test { int leaf_down = 0, inner_down = 0, parent_down = 0; parked sa, sb, sc; - auto branch = [](parked* s, destroy_count_t) -> task> + auto branch = [](parked* s, destroy_count_t) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; - auto inner = [&](destroy_count_t) -> task> + auto inner = [&](destroy_count_t) -> task> { std::ignore = co_await when_all( branch(&sb, destroy_count_t(leaf_down)), branch(&sc, destroy_count_t(leaf_down))); - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1574,14 +1574,14 @@ struct when_all_io_result_test int parent_down = 0; async_event ev; std::stop_source src; - auto branch = [&]() -> task> + auto branch = [&]() -> task> { auto tok = (co_await this_coro::environment)->stop_token; // Registered before the wait's, so libstdc++ runs it // after the wait's and relocks the stop state between. std::stop_callback extra(tok, [] {}); auto [ec] = co_await ev.wait(); - co_return io_result<>{ec}; + co_return results<>{ec}; }; auto parent = [&](destroy_count_t) -> task { @@ -1609,10 +1609,10 @@ struct when_all_io_result_test { co_await park_awaitable{s}; }; - auto hop = [&](parked* s, destroy_count_t) -> task> + auto hop = [&](parked* s, destroy_count_t) -> task> { co_await capy::run(inner)(leaf(s, destroy_count_t(leaf_down))); - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1649,15 +1649,15 @@ struct when_all_io_result_test std::queue q; bool closed = false; int owner_down = 0; - auto branch = []() -> task> + auto branch = []() -> task> { - co_return io_result<>{}; + co_return results<>{}; }; auto join = [&]() -> task { if(range) { - std::vector>> tasks; + std::vector>> tasks; tasks.push_back(branch()); tasks.push_back(branch()); std::ignore = co_await when_all(std::move(tasks)); @@ -1693,12 +1693,12 @@ struct when_all_io_result_test parked sa, sb; std::stop_token seen; auto branch = [](parked* s, std::stop_token* out) - -> task> + -> task> { if(out) *out = (co_await this_coro::environment)->stop_token; co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&]() -> task { @@ -1753,8 +1753,8 @@ struct when_all_io_result_test }; TEST_SUITE( - when_all_io_result_test, - "boost.capy.when_all_io_result"); + when_all_results_test, + "boost.capy.when_all_results"); } // capy } // boost diff --git a/test/unit/when_any.cpp b/test/unit/when_any.cpp index c207a3bf2..68d472bfb 100644 --- a/test/unit/when_any.cpp +++ b/test/unit/when_any.cpp @@ -18,7 +18,7 @@ #include #include #include -#include +#include #include #include "test_helpers.hpp" @@ -39,22 +39,22 @@ namespace capy { namespace { -io_task -io_success_size(size_t n) +task> +results_success_size(size_t n) { - co_return io_result{std::error_code(), n}; + co_return results{std::error_code(), n}; } -io_task -io_error_size(std::error_code ec, size_t n = 0) +task> +results_error_size(std::error_code ec, size_t n = 0) { - co_return io_result{ec, n}; + co_return results{ec, n}; } -io_task -io_success_string(std::string s) +task> +results_success_string(std::string s) { - co_return io_result{std::error_code(), std::move(s)}; + co_return results{std::error_code(), std::move(s)}; } #if defined(_MSC_VER) @@ -62,11 +62,11 @@ io_success_string(std::string s) #pragma warning(disable : 4702) // unreachable code after throw #endif -io_task -io_throws_size(char const* msg) +task> +results_throws_size(char const* msg) { throw test_exception(msg); - co_return io_result{std::error_code(), 0}; + co_return results{std::error_code(), 0}; } #if defined(_MSC_VER) @@ -74,20 +74,20 @@ io_throws_size(char const* msg) #endif // Suspends until stop token fires, then returns ECANCELED. -io_task -io_pending_size() +task> +results_pending_size() { co_await stop_only_awaitable{}; - co_return io_result{make_error_code(error::canceled), 0}; + co_return results{make_error_code(error::canceled), 0}; } // Awaitable that completes immediately (await_ready = true) -// returning a successful io_result. -struct immediate_io_awaitable +// returning a successful results. +struct immediate_context_awaitable { size_t n_; - explicit immediate_io_awaitable(size_t n) noexcept + explicit immediate_context_awaitable(size_t n) noexcept : n_(n) { } @@ -95,21 +95,21 @@ struct immediate_io_awaitable bool await_ready() const noexcept { return true; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<>, io_env const*) + await_suspend(std::coroutine_handle<>, await_context const*) { return std::noop_coroutine(); } - io_result await_resume() + results await_resume() { - return io_result{std::error_code(), n_}; + return results{std::error_code(), n_}; } }; int g_throwing_move_count = 0; // A payload whose move constructor throws on the third and later moves. -// The io_result constructor performs the first move and the branch +// The results constructor performs the first move and the branch // taking the awaited result the second; the extract into when_any's // result variant is the third, inside when_any's try/catch, exercising // the winner-exception path. @@ -132,38 +132,38 @@ struct throwing_payload_awaitable // thunk is exercised for this awaitable type. bool await_ready() const noexcept { return false; } std::coroutine_handle<> - await_suspend(std::coroutine_handle<> h, io_env const*) + await_suspend(std::coroutine_handle<> h, await_context const*) { return h; } - io_result await_resume() + results await_resume() { - return io_result{ + return results{ std::error_code(), throwing_move_payload{7}}; } }; -io_task<> -io_void_ok() +task> +results_void_ok() { - co_return io_result<>{}; + co_return results<>{}; } -io_task<> -io_void_error(std::error_code ec) +task> +results_void_error(std::error_code ec) { - co_return io_result<>{ec}; + co_return results<>{ec}; } #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable : 4702) // unreachable code after throw #endif -io_task<> -io_throws_void(char const* msg) +task> +results_throws_void(char const* msg) { throw test_exception(msg); - co_return io_result<>{}; + co_return results<>{}; } #if defined(_MSC_VER) #pragma warning(pop) @@ -180,8 +180,8 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_size(42)); + std::vector>> tasks; + tasks.push_back(results_success_size(42)); run_async(ex, [&](std::variant> v) { @@ -204,10 +204,10 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_success_size(10)); - tasks.push_back(io_success_size(20)); - tasks.push_back(io_success_size(30)); + std::vector>> tasks; + tasks.push_back(results_success_size(10)); + tasks.push_back(results_success_size(20)); + tasks.push_back(results_success_size(30)); run_async(ex, [&](std::variant> v) { @@ -234,7 +234,7 @@ struct when_any_vector_test stashing_range tasks([](std::size_t i) { - return io_success_size((i + 1) * 10); + return results_success_size((i + 1) * 10); }, 3); static_assert(!std::ranges::forward_range); @@ -263,7 +263,7 @@ struct when_any_vector_test auto tasks = std::views::iota(1, 4) | std::views::transform([](int i) { - return io_success_size(static_cast(i * 10)); + return results_success_size(static_cast(i * 10)); }); run_async(ex, @@ -286,7 +286,7 @@ struct when_any_vector_test test_executor ex(dc); bool caught = false; - std::vector> tasks; + std::vector>> tasks; run_async(ex, [](std::variant>) {}, @@ -308,10 +308,10 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_ok()); - tasks.push_back(io_void_ok()); + std::vector>> tasks; + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_ok()); + tasks.push_back(results_void_ok()); run_async(ex, [&](std::variant v) { @@ -337,9 +337,9 @@ struct when_any_vector_test bool caught = false; std::string msg; - std::vector> tasks; - tasks.push_back(io_throws_void("first")); - tasks.push_back(io_throws_void("second")); + std::vector>> tasks; + tasks.push_back(results_throws_void("first")); + tasks.push_back(results_throws_void("second")); run_async(ex, [&](std::variant) { @@ -362,9 +362,9 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_error_size(make_error_code(error::eof))); - tasks.push_back(io_success_size(100)); + std::vector>> tasks; + tasks.push_back(results_error_size(make_error_code(error::eof))); + tasks.push_back(results_success_size(100)); run_async(ex, [&](std::variant> v) { @@ -387,9 +387,9 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_error_size(make_error_code(error::eof))); - tasks.push_back(io_error_size(make_error_code(error::timeout))); + std::vector>> tasks; + tasks.push_back(results_error_size(make_error_code(error::eof))); + tasks.push_back(results_error_size(make_error_code(error::timeout))); run_async(ex, [&](std::variant> v) { @@ -412,9 +412,9 @@ struct when_any_vector_test bool caught = false; std::string msg; - std::vector> tasks; - tasks.push_back(io_throws_size("first")); - tasks.push_back(io_throws_size("second")); + std::vector>> tasks; + tasks.push_back(results_throws_size("first")); + tasks.push_back(results_throws_size("second")); run_async(ex, [&](std::variant>) { @@ -440,9 +440,9 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_throws_size("boom")); - tasks.push_back(io_success_size(55)); + std::vector>> tasks; + tasks.push_back(results_throws_size("boom")); + tasks.push_back(results_success_size(55)); run_async(ex, [&](std::variant> v) { @@ -467,9 +467,9 @@ struct when_any_vector_test bool completed = false; std::error_code result_ec; - std::vector> tasks; - tasks.push_back(io_throws_size("exception")); - tasks.push_back(io_error_size(make_error_code(error::eof))); + std::vector>> tasks; + tasks.push_back(results_throws_size("exception")); + tasks.push_back(results_error_size(make_error_code(error::eof))); run_async(ex, [&](std::variant> v) { @@ -491,9 +491,9 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; - tasks.push_back(io_void_error(make_error_code(error::eof))); - tasks.push_back(io_void_ok()); + std::vector>> tasks; + tasks.push_back(results_void_error(make_error_code(error::eof))); + tasks.push_back(results_void_ok()); run_async(ex, [&](std::variant v) { @@ -515,12 +515,12 @@ struct when_any_vector_test std::atomic completion_count{0}; bool completed = false; - auto counting = [&](size_t value) -> io_task { + auto counting = [&](size_t value) -> task> { ++completion_count; - co_return io_result{std::error_code(), value}; + co_return results{std::error_code(), value}; }; - std::vector> tasks; + std::vector>> tasks; tasks.push_back(counting(1)); tasks.push_back(counting(2)); tasks.push_back(counting(3)); @@ -547,25 +547,25 @@ struct when_any_vector_test std::atomic completed_normally_count{0}; bool when_any_completed = false; - auto fast = [&]() -> io_task { + auto fast = [&]() -> task> { ++completed_normally_count; - co_return io_result{std::error_code(), 42}; + co_return results{std::error_code(), 42}; }; - auto slow = [&](size_t id, int steps) -> io_task { + auto slow = [&](size_t id, int steps) -> task> { for (int i = 0; i < steps; ++i) { auto token = (co_await this_coro::environment)->stop_token; if (token.stop_requested()) { ++cancelled_count; - co_return io_result{make_error_code(error::canceled), 0}; + co_return results{make_error_code(error::canceled), 0}; } co_await yield_awaitable{}; } ++completed_normally_count; - co_return io_result{std::error_code(), id}; + co_return results{std::error_code(), id}; }; - std::vector> tasks; + std::vector>> tasks; tasks.push_back(fast()); tasks.push_back(slow(100, 10)); tasks.push_back(slow(200, 10)); @@ -595,9 +595,9 @@ struct when_any_vector_test test_executor ex(dc); bool completed = false; - std::vector> tasks; + std::vector>> tasks; for (size_t i = 1; i <= 20; ++i) - tasks.push_back(io_success_size(i)); + tasks.push_back(results_success_size(i)); run_async(ex, [&](std::variant> v) { @@ -621,17 +621,17 @@ struct when_any_vector_test bool completed = false; size_t result = 0; - auto inner = []() -> io_task { - std::vector> tasks; - tasks.push_back(io_success_size(10)); - tasks.push_back(io_success_size(20)); + auto inner = []() -> task> { + std::vector>> tasks; + tasks.push_back(results_success_size(10)); + tasks.push_back(results_success_size(20)); auto v = co_await when_any(std::move(tasks)); if(v.index() == 1) - co_return io_result{std::error_code(), std::get<1>(v).second}; - co_return io_result{std::get<0>(v), 0}; + co_return results{std::error_code(), std::get<1>(v).second}; + co_return results{std::get<0>(v), 0}; }; - std::vector> outer_tasks; + std::vector>> outer_tasks; outer_tasks.push_back(inner()); outer_tasks.push_back(inner()); @@ -678,9 +678,9 @@ TEST_SUITE( when_any_vector_test, "boost.capy.when_any_vector"); -// Tests for io_result-aware when_any behavior per the combinators spec. +// Tests for results-aware when_any behavior per the combinators spec. // Each test is labelled with the spec row it verifies. -struct when_any_io_result_test +struct when_any_results_test { // Spec Row 1: First task to return !ec // Wins. Cancel siblings. Return winner's result. @@ -702,8 +702,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_success_size(42), - io_pending_size())); + results_success_size(42), + results_pending_size())); BOOST_TEST(completed); // Child 0 succeeded -> variant at index 1 @@ -726,7 +726,7 @@ struct when_any_io_result_test BOOST_TEST_EQ(std::get<1>(v), 99u); }, [](std::exception_ptr) {})( - when_any(io_success_size(99))); + when_any(results_success_size(99))); BOOST_TEST(completed); } @@ -750,8 +750,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof)), - io_success_size(100))); + results_error_size(make_error_code(error::eof)), + results_success_size(100))); BOOST_TEST(completed); // Child 0 failed, child 1 won -> index 2 @@ -778,8 +778,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof)), - io_success_size(77))); + results_error_size(make_error_code(error::eof)), + results_success_size(77))); BOOST_TEST(completed); BOOST_TEST_EQ(winner_index, 2u); @@ -805,8 +805,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof)), - io_error_size(make_error_code(error::timeout)))); + results_error_size(make_error_code(error::eof)), + results_error_size(make_error_code(error::timeout)))); BOOST_TEST(completed); } @@ -830,8 +830,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_throws_size("boom"), - io_success_size(55))); + results_throws_size("boom"), + results_success_size(55))); BOOST_TEST(completed); // Child 0 threw (discarded), child 1 won -> index 2 @@ -860,8 +860,8 @@ struct when_any_io_result_test msg = e.what(); } })(when_any( - io_throws_size("first"), - io_throws_size("second"))); + results_throws_size("first"), + results_throws_size("second"))); BOOST_TEST(!completed); BOOST_TEST(caught); @@ -886,8 +886,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::canceled)), - io_error_size(make_error_code(error::canceled)))); + results_error_size(make_error_code(error::canceled)), + results_error_size(make_error_code(error::canceled)))); BOOST_TEST(completed); } @@ -909,8 +909,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof), 0), - io_success_size(200))); + results_error_size(make_error_code(error::eof), 0), + results_success_size(200))); BOOST_TEST(completed); // EOF didn't win; child 1 (success) won -> index 2 @@ -935,8 +935,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - immediate_io_awaitable(77), - io_pending_size())); + immediate_context_awaitable(77), + results_pending_size())); BOOST_TEST(completed); } @@ -958,8 +958,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_success_size(42), - io_success_string("hello"))); + results_success_size(42), + results_success_string("hello"))); BOOST_TEST(completed); } @@ -980,7 +980,7 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof)))); + results_error_size(make_error_code(error::eof)))); BOOST_TEST(completed); } @@ -1002,8 +1002,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_throws_size("exception"), - io_error_size(make_error_code(error::eof)))); + results_throws_size("exception"), + results_error_size(make_error_code(error::eof)))); BOOST_TEST(completed); BOOST_TEST(result_ec == cond::eof); @@ -1025,8 +1025,8 @@ struct when_any_io_result_test }, [](std::exception_ptr) {})( when_any( - io_error_size(make_error_code(error::eof)), - io_success_size(42))); + results_error_size(make_error_code(error::eof)), + results_success_size(42))); BOOST_TEST(completed); } @@ -1050,7 +1050,7 @@ struct when_any_io_result_test done = true; }, [](std::exception_ptr) {})( - when_any(io_pending_size(), io_pending_size())); + when_any(results_pending_size(), results_pending_size())); BOOST_TEST(done); } @@ -1084,9 +1084,9 @@ struct when_any_io_result_test { int a_down = 0, b_down = 0, parent_down = 0; std::queue q; - auto branch = [](destroy_count_t) -> task> + auto branch = [](destroy_count_t) -> task> { - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1129,10 +1129,10 @@ struct when_any_io_result_test parked sa, sb; test_executor ex(n); auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto join = [&]() -> task { @@ -1164,14 +1164,14 @@ struct when_any_io_result_test int down[count] = {}, parent_down = 0; parked slots[count]; auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { - std::vector>> tasks; + std::vector>> tasks; for(int i = 0; i < count; ++i) tasks.push_back( branch(&slots[i], destroy_count_t(down[i]))); @@ -1197,10 +1197,10 @@ struct when_any_io_result_test int parent_down = 0; bool resumed = false; parked sa, sb; - auto branch = [](parked* s) -> task> + auto branch = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1225,10 +1225,10 @@ struct when_any_io_result_test int a_down = 0, b_down = 0, parent_down = 0; parked sa, sb; auto branch = [](parked* s, destroy_count_t) - -> task> + -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1255,15 +1255,15 @@ struct when_any_io_result_test int parent_down = 0; bool resumed = false; parked sa, sb; - auto park = [](parked* s) -> task> + auto park = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; - auto failing = [](parked* s) -> task> + auto failing = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{make_error_code(error::canceled)}; + co_return results<>{make_error_code(error::canceled)}; }; auto parent = [&](destroy_count_t) -> task { @@ -1286,10 +1286,10 @@ struct when_any_io_result_test int parent_down = 0; bool resumed = false; parked sa, sb; - auto branch = [](parked* s) -> task> + auto branch = [](parked* s) -> task> { co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&](destroy_count_t) -> task { @@ -1308,9 +1308,9 @@ struct when_any_io_result_test // Parks the when_any runner itself, so tearing it down enters // through the runner's promise rather than through a branch task. - struct park_io_awaitable : park_awaitable + struct park_context_awaitable : park_awaitable { - io_result<> await_resume() const noexcept { return {}; } + results<> await_resume() const noexcept { return {}; } }; // Losing every branch under run_async(ex, alloc) frees the runner @@ -1324,7 +1324,7 @@ struct when_any_io_result_test parked* b, destroy_count_t) -> task { std::ignore = co_await when_any( - park_io_awaitable{{a}}, park_io_awaitable{{b}}); + park_context_awaitable{{a}}, park_context_awaitable{{b}}); }; int n = 0; test_executor ex(n); @@ -1348,14 +1348,14 @@ struct when_any_io_result_test int parent_down = 0; async_event ev; std::stop_source src; - auto branch = [&]() -> task> + auto branch = [&]() -> task> { auto tok = (co_await this_coro::environment)->stop_token; // Registered before the wait's, so libstdc++ runs it // after the wait's and relocks the stop state between. std::stop_callback extra(tok, [] {}); auto [ec] = co_await ev.wait(); - co_return io_result<>{ec}; + co_return results<>{ec}; }; auto parent = [&](destroy_count_t) -> task { @@ -1380,15 +1380,15 @@ struct when_any_io_result_test std::queue q; bool closed = false; int owner_down = 0; - auto branch = []() -> task> + auto branch = []() -> task> { - co_return io_result<>{}; + co_return results<>{}; }; auto join = [&]() -> task { if(range) { - std::vector>> tasks; + std::vector>> tasks; tasks.push_back(branch()); tasks.push_back(branch()); std::ignore = co_await when_any(std::move(tasks)); @@ -1424,12 +1424,12 @@ struct when_any_io_result_test parked sa, sb; std::stop_token seen; auto branch = [](parked* s, std::stop_token* out) - -> task> + -> task> { if(out) *out = (co_await this_coro::environment)->stop_token; co_await park_awaitable{s}; - co_return io_result<>{}; + co_return results<>{}; }; auto parent = [&]() -> task { @@ -1479,8 +1479,8 @@ struct when_any_io_result_test }; TEST_SUITE( - when_any_io_result_test, - "boost.capy.when_any_io_result"); + when_any_results_test, + "boost.capy.when_any_results"); } // capy } // boost diff --git a/test/unit/write.cpp b/test/unit/write.cpp index 5806c366b..2adccee4a 100644 --- a/test/unit/write.cpp +++ b/test/unit/write.cpp @@ -140,9 +140,9 @@ struct contingent_write_stream bool await_ready() const noexcept { return true; } void await_suspend( - std::coroutine_handle<>, io_env const*) const noexcept {} + std::coroutine_handle<>, await_context const*) const noexcept {} - io_result + results await_resume() { std::size_t const cap = buffer_size(buffers_); diff --git a/test/unit/write_at_least.cpp b/test/unit/write_at_least.cpp index 04dcf0e05..c51504fc3 100644 --- a/test/unit/write_at_least.cpp +++ b/test/unit/write_at_least.cpp @@ -66,9 +66,9 @@ struct contingent_write_stream bool await_ready() const noexcept { return true; } void await_suspend( - std::coroutine_handle<>, io_env const*) const noexcept {} + std::coroutine_handle<>, await_context const*) const noexcept {} - io_result + results await_resume() { std::size_t const cap = buffer_size(buffers_);