From e1111a5962cf18e71c27d992be2c525a7ed88d4d Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sat, 5 Sep 2026 21:08:24 +0200 Subject: [PATCH 1/2] docs: make intro page follow Robert Ramey's guidelines This changes the intro page in the docs to be very short and up to the point. Following Robert Ramey's guidelines, the purpose of this page is to convince the user that sees this library for the first time, in ~1 minute that it is wort spending next ~15 minutes reading the overview of this library. --- doc/antora.yml | 1 + .../ROOT/pages/4.coroutines/4a.tasks.adoc | 11 ++ .../Ac.contingencies.adoc | 4 +- doc/modules/ROOT/pages/index.adoc | 119 ++++-------------- doc/modules/ROOT/pages/quick-start.adoc | 23 +++- doc/modules/ROOT/pages/why-capy.adoc | 5 + test/doc/programs/index_page_echo.cpp | 15 ++- 7 files changed, 71 insertions(+), 107 deletions(-) diff --git a/doc/antora.yml b/doc/antora.yml index f06147b2b..32798ad2d 100644 --- a/doc/antora.yml +++ b/doc/antora.yml @@ -18,6 +18,7 @@ asciidoc: table-caption: false page-toc: '' toclevels: 2 + icons: font nav: - modules/ROOT/nav.adoc ext: diff --git a/doc/modules/ROOT/pages/4.coroutines/4a.tasks.adoc b/doc/modules/ROOT/pages/4.coroutines/4a.tasks.adoc index fa34a5612..baab2333f 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4a.tasks.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4a.tasks.adoc @@ -17,6 +17,17 @@ include::example$snippets/4a_tasks.cpp[tag=include_task] include::example$snippets/4a_tasks.cpp[tag=include_umbrella] ---- +Unless otherwise specified, all code examples in this documentation assume the following: + +[source,cpp] +---- +include::example$snippets/index_page.cpp[tag=convention] +---- + +The examples deliberately leave some results and bindings unused, so the +surrounding prose can explain them. Do not build them with `-Wall` or +`-Werror`. + ==== == Overview 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 9793b7d35..f5d0a3264 100644 --- a/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc +++ b/doc/modules/ROOT/pages/A.specification-methods/Ac.contingencies.adoc @@ -32,8 +32,8 @@ NOTE: Reaching the end of stream is also a contingency (which can be interpreted as preventing an infinite read from proceeding). -NOTE: The stream operations can still throw exceptions to indicate conditions - unrelated to stream state that prevent these operations from satisfying +NOTE: The stream operations can still throw exceptions to indicate conditions, + unrelated to stream state, that prevent these operations from satisfying their postconditions, such as failures to grow a buffer, or failure to allocate a coroutine frame. diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 84556564c..3dd2ebd28 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -1,110 +1,34 @@ = Capy :page-mode: explanation -Capy abstracts away sockets, files, and asynchrony with type-erased streams and buffer sequences—code compiles fast because the implementation is hidden. It provides the framework for concurrent algorithms that transact in buffers of memory: networking, serial ports, console, timers, and any platform I/O. This is only possible because Capy is coroutine-only, enabling optimizations and ergonomics that hybrid approaches must sacrifice. +Capy is a C++20, compiled library providing abstractions, vocabulary types and idioms necessary +to write programs with event-driven control flows +-- such as I/O -- in a way that makes them efficient and manageable: structured and hard to get wrong. -== What Capy Is +Coroutines are used to represent the control flow. The program logic is described in a sequential way, +familiar to programmers, +even though "continutions" are being registered in the execution environment behind the scenes. -Capy is two things at once: +I/O operations ar represented via a concept of a _stream_ which consumes and populates _buffer sequences_. +You also get type-erased wrappers for streams, so that the programs can compile fast, without triggering +many template instantiations. -* *A protocol.* cpp:IoAwaitable[] is a protocol for propagating a coroutine's _execution environment_—its executor, stop token, and allocator—forward through `co_await` chains. This is the vocabulary that lets awaitable-based coroutine libraries interoperate. -* *A reference implementation.* A concrete library—thread pool, task types, byte streams, buffer sequences, synchronization primitives—that proves the protocol works in practice. +It offers a number of task synchronization mechanisms, ranging from low-level, like async mutexes and events, +to high-level, like _strands_, `when_all`/`when_any` "joins", and resume-on-the-same-executor guarantee. -The protocol is the smaller, more general library living inside Capy. Without a shared protocol, _N_ coroutine libraries need _N_×(_N_−1) adapters to interoperate. With one shared protocol for environment propagation, a single bridge covers everyone. +Libraries that want to provide concrete I/O implementations -- like those based on `epoll` or `io_uring` -- +can plug to Capy's system via the `IoAwaitable`-protocol, which describes how the tasks can be scheduled, stopped +and have their memory allocated. -[IMPORTANT] -==== -*The core invariant: a coroutine always resumes on the executor it was started with.* +You also get the testing tools that allow you to test your asynchronous flows in a deterministic way. -Start a coroutine on a strand, and every resumption—after every `co_await`—happens on that strand. Shared state touched between suspension points is free of data races without a mutex. A _plain_ awaitable can resume a coroutine on any thread and would break this guarantee. Capy therefore rejects it at compile time, and provides an explicit way to bridge such awaitables when you need one. -See xref:4.coroutines/4c.executors.adoc#the-same-executor-invariant[the same-executor invariant] for the rationale and xref:4.coroutines/4d.io-awaitable.adoc#bridging-a-foreign-awaitable[bridging a foreign awaitable] for the escape hatch. -==== +== What Capy is Not -== What Capy Is Not +While capy offers interfaces for dealing with I/O, it does not process I/O itself. For that, you will neet +to use Capy in tandem with a Capy-conformant library. This can be either your home-grown one, or Corosio. -Capy is _not_ an all-purpose coroutine framework, and it is _not_ an implementation detail of Corosio. It is the execution model and byte-stream layer. It works standalone for logic that operates on streams without any platform I/O (HTTP parsing, protocol state machines, serialization). It also serves as the foundation for Corosio's networking layer. CERN's traccc project uses Capy without Corosio for GPU reconstruction pipelines; the Boost.HTTP parser is built entirely on Capy's byte streams. -== What This Library Does - -* *Lazy coroutine tasks* — cpp:task[task] with forward-propagating stop tokens and automatic cancellation -* *Buffer sequences* — taken straight from Asio and improved -* *Stream concepts* — three coroutine stream concepts: cpp:ReadStream[], cpp:WriteStream[], cpp:Stream[] -* *Type-erased streams* — cpp:any_stream[], cpp:any_read_stream[], cpp:any_write_stream[] for fast compilation -* *Concurrency facilities* — executors, strands, thread pools, cpp:when_all[], cpp:when_any[] -* *Test utilities* — mock streams, error injection - -== What This Library Does Not Do - -* *Networking* — no sockets, acceptors, or DNS; that's what Corosio provides -* *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` - -== Target Audience - -* Users of *Corosio* — portable coroutine networking -* Users of *Http* — sans-I/O HTTP/1.1 clients and servers -* Users of *Websocket* — sans-I/O WebSocket -* Users of *Beast2* — high-level HTTP/WebSocket servers -* Users of *Burl* — high-level HTTP client - -== The Library Family - -Capy is the foundation of a family of coroutine libraries. Each builds on Capy's execution model and byte streams to add a layer of the networking stack. The sibling libraries are in active development; their repositories may be incomplete or not yet released. - -* *Capy* — execution model, buffer sequences, and byte streams (this library) -* https://github.com/cppalliance/corosio[Corosio] — portable coroutine networking _(in development)_ -* https://github.com/cppalliance/http[Http] — sans-I/O HTTP/1.1 clients and servers _(in development)_ -* https://github.com/cppalliance/websocket[Websocket] — sans-I/O WebSocket _(in development)_ -* https://github.com/cppalliance/beast2[Beast2] — high-level HTTP/WebSocket servers _(in development)_ -* https://github.com/cppalliance/burl[Burl] — high-level HTTP client _(in development)_ - -== Design Philosophy - -* *Use case first.* Buffer sequences, stream concepts, executor affinity—these exist because I/O code needs them, not because they're theoretically elegant. -* *Coroutines-only.* No callbacks, futures, or sender/receiver. Hybrid support forces compromises; full commitment unlocks optimizations that adapted models cannot achieve. -* *Address the complaints of {cpp}.* Type erasure at boundaries, minimal dependencies, and hidden implementations keep builds fast and templates manageable. - -== Requirements - -=== Assumed Knowledge - -* {cpp}20 coroutines, concepts, and ranges -* Basic concurrent programming - -=== Compiler Support - -* GCC 12+ -* Clang 17+ -* Apple-Clang (macOS 14+) -* MSVC 14.34+ -* MinGW - -=== Dependencies - -None. Capy is self-contained and does not require Boost. - -=== Linking - -Capy is a compiled library. Link against `Boost::capy`. - -== Code Convention - -[NOTE] -==== -Unless otherwise specified, all code examples in this documentation assume the following: - -[source,cpp] ----- -include::example$snippets/index_page.cpp[tag=convention] ----- - -The examples deliberately leave some results and bindings unused, so the -surrounding prose can explain them. Do not build them with `-Wall` or -`-Werror`. -==== == Quick Example @@ -112,10 +36,11 @@ surrounding prose can explain them. Do not build them with `-Wall` or ---- include::example$programs/index_page_echo.cpp[tag=full] ---- +<.> The cpp:task[task<>] return type defines a coroutine that sarts suspended. `any_stream` is a type-erased wrapper that works with any concrete stream implementation. +<.> Each `co_await` suspends until the I/O operation completes. +<.> You can signal failures by throwing an exception, but also by returning a `std::error_code`. +<.> Status `cond::eof` indicates reaching the end of stream. -The `echo` function accepts an `any_stream&`—a type-erased wrapper that works with any concrete stream implementation. Each `co_await` suspends until the I/O completes. - -The cpp:task[task<>] return type (equivalent to `task`) creates a lazy coroutine that does not start executing until awaited or started with cpp:run_async[]. == Next Steps diff --git a/doc/modules/ROOT/pages/quick-start.adoc b/doc/modules/ROOT/pages/quick-start.adoc index d310d5e9d..fe81a49f9 100644 --- a/doc/modules/ROOT/pages/quick-start.adoc +++ b/doc/modules/ROOT/pages/quick-start.adoc @@ -10,7 +10,24 @@ = Quick Start :page-mode: tutorial -NOTE: Capy requires {cpp}20 with coroutine support. +== Requirements + +=== Assumed Knowledge + +* {cpp}20 coroutines, concepts, and ranges +* Basic concurrent programming + +=== Compiler Support + +* GCC 12+ +* Clang 17+ +* Apple-Clang (macOS 14+) +* MSVC 14.34+ +* MinGW + +=== Dependencies + +None. Capy is self-contained and does not require Boost. == Minimal Example @@ -38,7 +55,9 @@ g++ -std=c++20 -I/path/to/capy/include -o hello_coro hello_coro.cpp \ /path/to/capy/build/libboost_capy.a -pthread ---- -Then run it: +NOTE: If your prorgam is build with CMake, link it against `Boost::capy`. + +Then run the compiled program: [source,bash,role=external] ---- diff --git a/doc/modules/ROOT/pages/why-capy.adoc b/doc/modules/ROOT/pages/why-capy.adoc index ce5085de4..94f07c604 100644 --- a/doc/modules/ROOT/pages/why-capy.adoc +++ b/doc/modules/ROOT/pages/why-capy.adoc @@ -10,6 +10,11 @@ That restriction buys a guarantee the hybrid libraries cannot make: *a coroutine include::example$snippets/why_capy.cpp[tag=invariant] ---- +[NOTE] +==== +See xref:4.coroutines/4c.executors.adoc#the-same-executor-invariant[the same-executor invariant] for the rationale and xref:4.coroutines/4d.io-awaitable.adoc#bridging-a-foreign-awaitable[bridging a foreign awaitable] for the escape hatch. +==== + == What Capy Is Not Capy is not a networking library. It has no sockets, no acceptors, no DNS, no TLS, and no platform event loop. Those belong to Corosio, which is built on Capy. diff --git a/test/doc/programs/index_page_echo.cpp b/test/doc/programs/index_page_echo.cpp index 003dff7c1..33e45e948 100644 --- a/test/doc/programs/index_page_echo.cpp +++ b/test/doc/programs/index_page_echo.cpp @@ -15,20 +15,23 @@ namespace capy = boost::capy; -capy::task<> echo(capy::any_stream& stream) +capy::task echo(capy::any_stream& stream) // <.> { char buf[1024]; for(;;) { - auto [ec, n] = co_await stream.read_some(capy::make_buffer(buf)); + auto [ec, n] = co_await stream.read_some(capy::make_buffer(buf)); // <.> - auto [wec, wn] = co_await capy::write(stream, capy::const_buffer(buf, n)); - - if(ec) - co_return; + auto [wec, _] = co_await capy::write(stream, capy::const_buffer(buf, n)); if(wec) + throw std::system_error(wec); // <.> + + if(ec == capy::cond::eof) // <.> co_return; + + if(ec) + throw std::system_error(ec); } } From 2afe6c1b7011b4a55f3336e3cd1dc18b66214933 Mon Sep 17 00:00:00 2001 From: Andrzej Krzemienski Date: Sun, 6 Sep 2026 10:43:04 +0200 Subject: [PATCH 2/2] Vinnie's feedback --- doc/modules/ROOT/pages/index.adoc | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/doc/modules/ROOT/pages/index.adoc b/doc/modules/ROOT/pages/index.adoc index 3dd2ebd28..bc5af2cfb 100644 --- a/doc/modules/ROOT/pages/index.adoc +++ b/doc/modules/ROOT/pages/index.adoc @@ -1,32 +1,34 @@ = Capy :page-mode: explanation -Capy is a C++20, compiled library providing abstractions, vocabulary types and idioms necessary +Capy is a compiled C++20 library providing abstractions, vocabulary types, and idioms necessary to write programs with event-driven control flows -- such as I/O -- in a way that makes them efficient and manageable: structured and hard to get wrong. Coroutines are used to represent the control flow. The program logic is described in a sequential way, familiar to programmers, -even though "continutions" are being registered in the execution environment behind the scenes. +even though there are tasks being pushed to task queues behind the scenes. -I/O operations ar represented via a concept of a _stream_ which consumes and populates _buffer sequences_. -You also get type-erased wrappers for streams, so that the programs can compile fast, without triggering -many template instantiations. +I/O operations are represented via the concept of a _stream_ which consumes and populates _buffer sequences_. +You also get type-erased wrappers for streams, so that programs can compile quickly, without triggering +excessive template instantiations. It offers a number of task synchronization mechanisms, ranging from low-level, like async mutexes and events, -to high-level, like _strands_, `when_all`/`when_any` "joins", and resume-on-the-same-executor guarantee. +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 to Capy's system via the `IoAwaitable`-protocol, which describes how the tasks can be scheduled, stopped -and have their memory allocated. +can plug into Capy's system via the `IoAwaitable` 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. == What Capy is Not -While capy offers interfaces for dealing with I/O, it does not process I/O itself. For that, you will neet -to use Capy in tandem with a Capy-conformant library. This can be either your home-grown one, or Corosio. +While Capy offers algorithms and interfaces for dealing with I/O, +it does not itself provide any I/O backend (like a wrapper over `epoll` or `io_uring`). +For that, you will need to use Capy in tandem with a Capy-conformant library. +This can be either your home-grown one or Corosio. @@ -36,10 +38,10 @@ to use Capy in tandem with a Capy-conformant library. This can be either your ho ---- include::example$programs/index_page_echo.cpp[tag=full] ---- -<.> The cpp:task[task<>] return type defines a coroutine that sarts suspended. `any_stream` is a type-erased wrapper that works with any concrete stream implementation. +<.> The cpp:task[task<>] return type defines a coroutine that starts suspended. `any_stream` is a type-erased wrapper that works with any concrete stream implementation. <.> Each `co_await` suspends until the I/O operation completes. -<.> You can signal failures by throwing an exception, but also by returning a `std::error_code`. -<.> Status `cond::eof` indicates reaching the end of stream. +<.> You can signal failures by throwing an exception, or by returning a `std::error_code`. +<.> The condition `cond::eof` indicates reaching the end of the stream. == Next Steps