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 .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
/.vs
/build-*/
/build/*
!/build/Jamfile
Expand Down
4 changes: 2 additions & 2 deletions doc/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
* xref:why-not-cobalt-2.adoc[Why Not Cobalt Concepts?]
* xref:why-not-tmc.adoc[Why Not TooManyCooks?]
* xref:quick-start.adoc[Quick Start]
* xref:2.cpp20-coroutines/2.intro.adoc[Introduction To C++20 Coroutines]
* xref:2.cpp20-coroutines/2.intro.adoc[Introduction To {cpp}20 Coroutines]
** xref:2.cpp20-coroutines/2a.foundations.adoc[Part I: Foundations]
** xref:2.cpp20-coroutines/2b.syntax.adoc[Part II: C++20 Syntax]
** xref:2.cpp20-coroutines/2b.syntax.adoc[Part II: {cpp}20 Syntax]
** xref:2.cpp20-coroutines/2c.machinery.adoc[Part III: Coroutine Machinery]
** xref:2.cpp20-coroutines/2d.advanced.adoc[Part IV: Advanced Topics]
* xref:3.concurrency/3.intro.adoc[Introduction to Concurrency]
Expand Down
8 changes: 4 additions & 4 deletions doc/modules/ROOT/pages/2.cpp20-coroutines/2.intro.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,12 @@
// Official repository: https://github.com/cppalliance/capy
//

= Introduction To C++20 Coroutines
= Introduction To {cpp}20 Coroutines

Every C++ function you have ever written follows the same contract: it runs from start to finish, then returns. The caller waits. The stack frame lives and dies in lockstep with that single invocation. This model has served us well for decades, but it forces a hard tradeoff when programs need to wait--for a network response, a disk read, a timer, or another thread. The function either blocks (wasting a thread) or you restructure your code into callbacks, state machines, or futures that scatter your logic across multiple places.
Every {cpp} function you have ever written follows the same contract: it runs from start to finish, then returns. The caller waits. The stack frame lives and dies in lockstep with that single invocation. This model has served us well for decades, but it forces a hard tradeoff when programs need to wait--for a network response, a disk read, a timer, or another thread. The function either blocks (wasting a thread) or you restructure your code into callbacks, state machines, or futures that scatter your logic across multiple places.

C++20 coroutines change the rules. A coroutine can _suspend_ its execution--saving its local state somewhere outside the stack--and _resume_ later, picking up exactly where it left off. The control flow reads top-to-bottom, like the synchronous code you already know, but the runtime behavior is asynchronous. No blocked threads. No callback chains. No lost context.
{cpp}20 coroutines change the rules. A coroutine can _suspend_ its execution--saving its local state somewhere outside the stack--and _resume_ later, picking up exactly where it left off. The control flow reads top-to-bottom, like the synchronous code you already know, but the runtime behavior is asynchronous. No blocked threads. No callback chains. No lost context.

This is not a minor syntactic convenience. It is a fundamental shift in how you can structure programs that wait.

This section takes you from zero to a working understanding of C++20 coroutines. No prior experience with coroutines or async programming is needed. You will start with the problem that coroutines solve, move through the language syntax and compiler machinery, and finish with the performance characteristics that make coroutines practical for real systems. By the end, you will understand not only _how_ to write coroutines but _why_ they work the way they do--knowledge that will make everything in the rest of this documentation click into place.
This section takes you from zero to a working understanding of {cpp}20 coroutines. No prior experience with coroutines or async programming is needed. You will start with the problem that coroutines solve, move through the language syntax and compiler machinery, and finish with the performance characteristics that make coroutines practical for real systems. By the end, you will understand not only _how_ to write coroutines but _why_ they work the way they do--knowledge that will make everything in the rest of this documentation click into place.
20 changes: 10 additions & 10 deletions doc/modules/ROOT/pages/2.cpp20-coroutines/2a.foundations.adoc
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
= Part I: Foundations

This section introduces the fundamental concepts you need before working with C++20 coroutines. You will learn how normal functions work, what makes coroutines different, and why coroutines exist as a language feature.
This section introduces the fundamental concepts you need before working with {cpp}20 coroutines. You will learn how normal functions work, what makes coroutines different, and why coroutines exist as a language feature.

== Prerequisites

Before beginning this tutorial, you should have:

* A C++ compiler with C++20 support (GCC 10+, Clang 14+, or MSVC 2019 16.8+)
* Familiarity with basic C++ concepts: functions, classes, templates, and lambdas
* A {cpp} compiler with {cpp}20 support (GCC 10+, Clang 14+, or MSVC 2019 16.8+)
* Familiarity with basic {cpp} concepts: functions, classes, templates, and lambdas
* Understanding of how function calls work: the call stack, local variables, and return values

The examples in this tutorial use standard C++20 features. Compile with:
The examples in this tutorial use standard {cpp}20 features. Compile with:

* GCC: `g++ -std=c++20 -fcoroutines your_file.cpp`
* Clang: `clang++ -std=c++20 your_file.cpp`
Expand Down Expand Up @@ -42,7 +42,7 @@ This model has a fundamental constraint: *run-to-completion*. Once a function st

== What Is a Coroutine?

A *coroutine* is a function that can suspend its execution and resume later from exactly where it left off. Think of it as a bookmark in a book of instructionsinstead of reading the entire book in one sitting, you can mark your place, do something else, and return to continue reading.
A *coroutine* is a function that can suspend its execution and resume later from exactly where it left off. Think of it as a bookmark in a book of instructionsinstead of reading the entire book in one sitting, you can mark your place, do something else, and return to continue reading.

When a coroutine suspends:

Expand All @@ -55,7 +55,7 @@ When a coroutine resumes:
* Local variables are restored to their previous values
* Execution continues from the suspension point

This capability is implemented through a *coroutine frame*a heap-allocated block of memory that stores the coroutine's state. Unlike stack frames, coroutine frames persist across suspension points because they live on the heap rather than the stack.
This capability is implemented through a *coroutine frame*a heap-allocated block of memory that stores the coroutine's state. Unlike stack frames, coroutine frames persist across suspension points because they live on the heap rather than the stack.

[source,cpp]
----
Expand Down Expand Up @@ -138,8 +138,8 @@ This code reads like the original blocking version. Local variables like `reques

Coroutines also enable:

* *Generators* Functions that produce sequences of values on demand, computing each value only when requested
* *State machines* Complex control flow expressed as linear code with suspension points
* *Cooperative multitasking* Multiple logical tasks interleaved on a single thread
* *Generators* Functions that produce sequences of values on demand, computing each value only when requested
* *State machines* Complex control flow expressed as linear code with suspension points
* *Cooperative multitasking* Multiple logical tasks interleaved on a single thread

You have now learned what coroutines are and why they exist. In the next section, you will learn the C++20 syntax for creating coroutines.
You have now learned what coroutines are and why they exist. In the next section, you will learn the {cpp}20 syntax for creating coroutines.
22 changes: 11 additions & 11 deletions doc/modules/ROOT/pages/2.cpp20-coroutines/2b.syntax.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
= Part II: C++20 Syntax
= Part II: {cpp}20 Syntax

This section introduces the three C++20 keywords that create coroutines and walks you through building your first coroutine step by step.
This section introduces the three {cpp}20 keywords that create coroutines and walks you through building your first coroutine step by step.

== Prerequisites

Expand All @@ -26,7 +26,7 @@ task<std::string> fetch_page(std::string url)

=== co_yield

The `co_yield` keyword produces a value and suspends the coroutine. This pattern creates *generators*functions that produce sequences of values one at a time. After yielding a value, the coroutine pauses until someone asks for the next value.
The `co_yield` keyword produces a value and suspends the coroutine. This pattern creates *generators*functions that produce sequences of values one at a time. After yielding a value, the coroutine pauses until someone asks for the next value.

[source,cpp]
----
Expand Down Expand Up @@ -91,11 +91,11 @@ For now, observe that the presence of `co_return` transforms what looks like a r

== Awaitables and Awaiters

When you write `co_await expr`, the expression `expr` must be an *awaitable*something that knows how to suspend and resume a coroutine. The awaitable produces an *awaiter* object that implements three methods:
When you write `co_await expr`, the expression `expr` must be an *awaitable*something that knows how to suspend and resume a coroutine. The awaitable produces an *awaiter* object that implements three methods:

* `await_ready()` Returns `true` if the result is immediately available and no suspension is needed
* `await_suspend(handle)` Called when the coroutine suspends; receives a handle to the coroutine for later resumption
* `await_resume()` Called when the coroutine resumes; its return value becomes the value of the `co_await` expression
* `await_ready()` Returns `true` if the result is immediately available and no suspension is needed
* `await_suspend(handle)` Called when the coroutine suspends; receives a handle to the coroutine for later resumption
* `await_resume()` Called when the coroutine resumes; its return value becomes the value of the `co_await` expression

=== Example: Understanding the Awaiter Protocol

Expand Down Expand Up @@ -183,10 +183,10 @@ The variable `i` inside `counter` maintains its value across all these suspensio

=== Standard Awaiters

The C++ standard library provides two predefined awaiters:
The {cpp} standard library provides two predefined awaiters:

* `std::suspend_always` `await_ready()` returns `false` (always suspend)
* `std::suspend_never` `await_ready()` returns `true` (never suspend)
* `std::suspend_always` `await_ready()` returns `false` (always suspend)
* `std::suspend_never` `await_ready()` returns `true` (never suspend)

These are useful building blocks for promise types and custom awaitables.

Expand All @@ -199,4 +199,4 @@ co_await std::suspend_always{};
co_await std::suspend_never{};
----

You have now learned the three coroutine keywords and how awaitables work. In the next section, you will learn about the promise type and coroutine handlethe machinery that makes coroutines function.
You have now learned the three coroutine keywords and how awaitables work. In the next section, you will learn about the promise type and coroutine handlethe machinery that makes coroutines function.
16 changes: 8 additions & 8 deletions doc/modules/ROOT/pages/2.cpp20-coroutines/2c.machinery.adoc
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
= Part III: Coroutine Machinery

This section explains the promise type and coroutine handlethe core machinery that controls coroutine behavior. You will build a complete generator type by understanding how these pieces work together.
This section explains the promise type and coroutine handlethe core machinery that controls coroutine behavior. You will build a complete generator type by understanding how these pieces work together.

== Prerequisites

* Completed xref:2b.syntax.adoc[Part II: C++20 Syntax]
* Completed xref:2b.syntax.adoc[Part II: {cpp}20 Syntax]
* Understanding of the three coroutine keywords
* Familiarity with awaitables and awaiters

== The Promise Type

Every coroutine has an associated *promise type*. This type acts as a controller for the coroutine, defining how it behaves at key points in its lifecycle. The promise type is not something you pass to the coroutineit is a nested type inside the coroutine's return type that the compiler uses automatically.
Every coroutine has an associated *promise type*. This type acts as a controller for the coroutine, defining how it behaves at key points in its lifecycle. The promise type is not something you pass to the coroutineit is a nested type inside the coroutine's return type that the compiler uses automatically.

The compiler expects to find a type named `promise_type` nested inside your coroutine's return type. If your coroutine returns `Generator<int>`, the compiler looks for `Generator<int>::promise_type`.

Expand Down Expand Up @@ -60,7 +60,7 @@ The compiler transforms your coroutine body into something resembling this pseud
Important observations:

* The return object is created before `initial_suspend()` runs, so it is available even if the coroutine suspends immediately
* `final_suspend()` determines whether the coroutine frame persists after completionif it returns `suspend_always`, you must manually destroy the coroutine; if it returns `suspend_never`, the frame is destroyed automatically
* `final_suspend()` determines whether the coroutine frame persists after completionif it returns `suspend_always`, you must manually destroy the coroutine; if it returns `suspend_never`, the frame is destroyed automatically

=== Tracing Promise Behavior

Expand Down Expand Up @@ -153,10 +153,10 @@ A `std::coroutine_handle<>` is a lightweight object that refers to a suspended c

=== Basic Operations

* `handle()` or `handle.resume()` Resume the coroutine
* `handle.done()` Returns `true` if the coroutine has completed
* `handle.destroy()` Destroy the coroutine frame (frees memory)
* `handle.promise()` Returns a reference to the promise object (typed handles only)
* `handle()` or `handle.resume()` Resume the coroutine
* `handle.done()` Returns `true` if the coroutine has completed
* `handle.destroy()` Destroy the coroutine frame (frees memory)
* `handle.promise()` Returns a reference to the promise object (typed handles only)

=== Typed vs Untyped Handles

Expand Down
18 changes: 9 additions & 9 deletions doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This section covers advanced coroutine topics: symmetric transfer for efficient

== Symmetric Transfer

When a coroutine completes or awaits another coroutine, control must transfer somewhere. The naive approachsimply calling `handle.resume()`has a problem: each nested coroutine adds a frame to the call stack. With deep nesting, you risk stack overflow.
When a coroutine completes or awaits another coroutine, control must transfer somewhere. The naive approachsimply calling `handle.resume()`has a problem: each nested coroutine adds a frame to the call stack. With deep nesting, you risk stack overflow.

*Symmetric transfer* solves this by returning a coroutine handle from `await_suspend`. Instead of resuming the target coroutine via a function call, the compiler generates a tail call that transfers control without growing the stack.

Expand Down Expand Up @@ -102,7 +102,7 @@ auto final_suspend() noexcept

== Coroutine Allocation

Every coroutine needs memory for its *coroutine frame*the heap-allocated structure holding local variables, parameters, and suspension state.
Every coroutine needs memory for its *coroutine frame*the heap-allocated structure holding local variables, parameters, and suspension state.

=== Default Allocation

Expand Down Expand Up @@ -454,13 +454,13 @@ This generator:

== Conclusion

You have now learned the complete mechanics of C++20 coroutines:
You have now learned the complete mechanics of {cpp}20 coroutines:

* *Keywords* `co_await`, `co_yield`, and `co_return` transform functions into coroutines
* *Promise types* Control coroutine behavior at initialization, suspension, completion, and error handling
* *Coroutine handles* Lightweight references for resuming, querying, and destroying coroutines
* *Symmetric transfer* Efficient control flow without stack accumulation
* *Allocation* Custom allocation and HALO optimization
* *Exception handling* Capturing and propagating exceptions across suspension points
* *Keywords* `co_await`, `co_yield`, and `co_return` transform functions into coroutines
* *Promise types* Control coroutine behavior at initialization, suspension, completion, and error handling
* *Coroutine handles* Lightweight references for resuming, querying, and destroying coroutines
* *Symmetric transfer* Efficient control flow without stack accumulation
* *Allocation* Custom allocation and HALO optimization
* *Exception handling* Capturing and propagating exceptions across suspension points

These fundamentals prepare you for understanding Capy's `task<T>` type and the IoAwaitable protocol, which build on standard coroutine machinery with executor affinity and stop token propagation.
6 changes: 3 additions & 3 deletions doc/modules/ROOT/pages/3.concurrency/3a.foundations.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ This section introduces the fundamental concepts of concurrent programming. You

Before beginning this tutorial, you should have:

* A C++ compiler with C++11 or later support
* Familiarity with basic C++ concepts: functions, classes, and lambdas
* A {cpp} compiler with {cpp}11 or later support
* Familiarity with basic {cpp} concepts: functions, classes, and lambdas
* Understanding of how programs execute sequentially

== Why Concurrency Matters
Expand All @@ -32,7 +32,7 @@ This sharing is both the power and the peril of threads.

== Creating Threads

The `<thread>` header provides `std::thread`, the standard way to create threads in C++.
The `<thread>` header provides `std::thread`, the standard way to create threads in {cpp}.

[source,cpp]
----
Expand Down
Loading