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 CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Look in context/ for information
24 changes: 22 additions & 2 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,2 +1,22 @@
* xref:coroutines.adoc[Coroutines]
* xref:reference:boost/capy.adoc[Reference]
* xref:index.adoc[Introduction]
* xref:quick-start.adoc[Quick Start]
* Coroutines
** xref:coroutines/tasks.adoc[Tasks]
** xref:coroutines/launching.adoc[Launching Tasks]
** xref:coroutines/affinity.adoc[Executor Affinity]
** xref:coroutines/cancellation.adoc[Cancellation]
* Execution
** xref:execution/executors.adoc[Executors]
** xref:execution/contexts.adoc[Execution Contexts]
** xref:execution/frame-allocation.adoc[Frame Allocation]
* Utilities
** xref:utilities/containers.adoc[Containers]
** xref:utilities/file-io.adoc[File I/O]
** xref:utilities/compression.adoc[Compression]
* Concepts
** xref:concepts/dispatcher.adoc[dispatcher]
** xref:concepts/executor.adoc[executor]
** xref:concepts/affine_awaitable.adoc[affine_awaitable]
** xref:concepts/stoppable_awaitable.adoc[stoppable_awaitable]
** xref:concepts/frame_allocator.adoc[frame_allocator]
** xref:concepts/is_execution_context.adoc[is_execution_context]
118 changes: 118 additions & 0 deletions doc/modules/ROOT/pages/concepts/affine_awaitable.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
//
// 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
//

= affine_awaitable

An awaitable is affine if it participates in the affine awaitable protocol
by accepting a dispatcher in its `await_suspend` method.

Requires: C++20

== Synopsis

Defined in header `<boost/capy/affine.hpp>`

[source,cpp]
----
namespace boost::capy {

template<typename A, typename D, typename P = void>
concept affine_awaitable =
dispatcher<D, P> &&
requires(A a, std::coroutine_handle<P> h, D const& d) {
a.await_suspend(h, d);
};

} // namespace boost::capy
----

== Description

The affine awaitable protocol enables zero-overhead scheduler affinity without
requiring the full sender/receiver protocol. When an awaitable is affine, it
receives the caller's dispatcher in `await_suspend` and uses it to resume the
caller on the correct execution context.

The awaitable must use the dispatcher `d` to resume the caller when the
operation completes. Typically this looks like `return d(h);` for symmetric
transfer or calling `d(h)` before returning `std::noop_coroutine()`.

== Preconditions

* The dispatcher `d` remains valid until the awaitable resumes the caller

== Valid Expressions

Given:

* `a` — a value of type `A`
* `h` — a value of type `std::coroutine_handle<P>`
* `d` — a const value of type `D` satisfying `dispatcher<D, P>`

[cols="2,1,3"]
|===
| Expression | Return Type | Description

| `a.await_ready()`
| `bool`
| Return `true` if the operation has already completed

| `a.await_suspend(h, d)`
| (unspecified)
| Suspend and start the async operation, using `d` for resumption

| `a.await_resume()`
| (unspecified)
| Return the operation result or rethrow any exception
|===

== Example

[source,cpp]
----
#include <boost/capy/affine.hpp>

using boost::capy::coro;
using boost::capy::affine_awaitable;
using boost::capy::any_dispatcher;

struct my_async_op
{
bool await_ready() const noexcept
{
return false;
}

template<typename Dispatcher>
auto await_suspend(coro h, Dispatcher const& d)
{
start_async([h, &d] {
// Operation completed, resume through dispatcher
d(h);
});
return std::noop_coroutine();
}

int await_resume()
{
return result_;
}

private:
int result_ = 42;
};

static_assert(affine_awaitable<my_async_op, any_dispatcher>);
----

== See Also

* xref:dispatcher.adoc[dispatcher] — The dispatcher concept
* xref:stoppable_awaitable.adoc[stoppable_awaitable] — Extended protocol with cancellation
* xref:../coroutines/affinity.adoc[Executor Affinity] — Tutorial on affinity propagation
100 changes: 100 additions & 0 deletions doc/modules/ROOT/pages/concepts/dispatcher.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//
// Copyright (c) 2025 Vinnie Falco (vinnie.falco@gmail.com)
//
// 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
//

= dispatcher

A dispatcher is a callable object that accepts a coroutine handle and
schedules it for resumption.

Requires: C++20

== Synopsis

Defined in header `<boost/capy/affine.hpp>`

[source,cpp]
----
namespace boost::capy {

template<typename D, typename P = void>
concept dispatcher = requires(D const& d, std::coroutine_handle<P> h) {
{ d(h) } -> std::convertible_to<coro>;
};

} // namespace boost::capy
----

== Description

A dispatcher encapsulates the rules for how and where a coroutine resumes.
When invoked with a coroutine handle, the dispatcher schedules the handle for
resumption and returns a handle suitable for symmetric transfer.

Dispatchers must be const-callable, enabling thread-safe concurrent dispatch
from multiple coroutines. The dispatcher may resume the handle inline (by
returning the handle itself) or queue it for later execution (by returning
`std::noop_coroutine()`).

Since `coro` (an alias for `std::coroutine_handle<void>`) has `operator()`
which invokes `resume()`, the handle itself is callable and can be dispatched
directly.

== Valid Expressions

Given:

* `d` — a const value of type `D`
* `h` — a value of type `std::coroutine_handle<P>`

[cols="2,1,3"]
|===
| Expression | Return Type | Description

| `d(h)`
| convertible to `coro`
| Schedule `h` for resumption and return a handle for symmetric transfer
|===

== Example

[source,cpp]
----
#include <boost/capy/affine.hpp>

using boost::capy::coro;
using boost::capy::dispatcher;

struct inline_dispatcher
{
coro operator()(coro h) const
{
return h; // Resume inline via symmetric transfer
}
};

struct queuing_dispatcher
{
work_queue* queue_;

coro operator()(coro h) const
{
queue_->push(h);
return std::noop_coroutine(); // Caller returns to event loop
}
};

static_assert(dispatcher<inline_dispatcher>);
static_assert(dispatcher<queuing_dispatcher>);
----

== See Also

* xref:affine_awaitable.adoc[affine_awaitable] — Awaitable that accepts a dispatcher
* xref:stoppable_awaitable.adoc[stoppable_awaitable] — Awaitable with cancellation support
* xref:../coroutines/affinity.adoc[Executor Affinity] — Tutorial on affinity propagation
Loading
Loading