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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/antora.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ asciidoc:
table-caption: false
page-toc: ''
toclevels: 2
icons: font
nav:
- modules/ROOT/nav.adoc
ext:
Expand Down
11 changes: 11 additions & 0 deletions doc/modules/ROOT/pages/4.coroutines/4a.tasks.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
121 changes: 24 additions & 97 deletions doc/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
@@ -1,121 +1,48 @@
= 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 compiled C++20 library providing abstractions, vocabulary types, and idioms necessary

Check warning on line 4 in doc/modules/ROOT/pages/index.adoc

View workflow job for this annotation

GitHub Actions / Antora Docs

[sentence_length C2] sentence over 25 words (39)
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 there are tasks being pushed to task queues behind the scenes.

Capy is two things at once:
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.

* *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,

Check warning on line 16 in doc/modules/ROOT/pages/index.adoc

View workflow job for this annotation

GitHub Actions / Antora Docs

[sentence_length C2] sentence over 25 words (27)
to high-level, like _strands_, `when_all`/`when_any` "joins", and the 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` --

Check warning on line 19 in doc/modules/ROOT/pages/index.adoc

View workflow job for this annotation

GitHub Actions / Antora Docs

[sentence_length C2] sentence over 25 words (36)
can plug into Capy's system via the `IoAwaitable` protocol, which describes how tasks can be scheduled,
cancelled, and allocated in memory.

[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 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.

Check warning on line 30 in doc/modules/ROOT/pages/index.adoc

View workflow job for this annotation

GitHub Actions / Antora Docs

[vale_adoc Capy.SimpleTense] Avoid needless future/perfect tense — prefer present simple (style guide C4): 'will '.
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<T>] 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

[source,cpp]
----
include::example$programs/index_page_echo.cpp[tag=full]
----
<.> 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, or by returning a `std::error_code`.
<.> The condition `cond::eof` indicates reaching the end of the 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<void>`) creates a lazy coroutine that does not start executing until awaited or started with cpp:run_async[].

== Next Steps

Expand Down
23 changes: 21 additions & 2 deletions doc/modules/ROOT/pages/quick-start.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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]
----
Expand Down
5 changes: 5 additions & 0 deletions doc/modules/ROOT/pages/why-capy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
15 changes: 9 additions & 6 deletions test/doc/programs/index_page_echo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,23 @@

namespace capy = boost::capy;

capy::task<> echo(capy::any_stream& stream)
capy::task<void> 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);
}
}

Expand Down
Loading