introduction_to_cpp_coroutine_task_schedule
Introduction to C++ Coroutines Through a Thread Scheduling Demonstration - Dian-Lun Lin
Coroutine is very useful if you have other computing resource (GPU, TPU, async I/O ...)
- Similar to managing tasks like boiling water and taking a shower concurrently at home, coroutines allow overlapping activities in computing, like CPU and GPU work.
- Enables efficient use of computing resources by allowing asynchronous operations and multitasking without blocking the main execution flow.
Identification of a Coroutine
- 2 conditions"
- Use of
co_wait,co_yield, orco_returninside a function makes it a coroutine. - Return Type: The function must return a coroutine-specific object that involves a promise.
- Use of
promise_type: manages the behavior of a coroutine

- Including:
- Suspension at the beginning (
initial_suspend) and end (final_suspend) of execution. - Creation of the coroutine object.
- Exception handling during execution.
- Suspension at the beginning (
Coroutine Suspension Mechanisms:
- Control the coroutine's points of suspension based on the execution's needs and external conditions.
- Initial Suspension (
initial_suspend):- Function: Determines if the coroutine suspends immediately after being invoked.
- Usage: Typically used to delay execution until conditions are favorable or resources are ready.
- Why a coroutine might need to suspend immediately upon creation.
- Provides the caller (e.g., a main function) the control to resume the coroutine at an appropriate time, enhancing flexibility and resource coordination.
- Final Suspension (
final_suspend):- Function: Decides if the coroutine should suspend after completing its tasks, before fully terminating.
- Purpose: Allows time for proper cleanup or result processing before the coroutine's resources are released.
- What's the rationale for a coroutine to suspend at its conclusion rather than terminating immediately.
- Keeps the coroutine's state intact until all operations are conclusively finished or results are harvested, which is particularly vital in asynchronous operations like GPU processing.
Awaitable: control the behavior at specific suspension points within a coroutine
- They determine when and how a coroutine should suspend or resume, based on conditions defined within the coroutine's logic.

Defining an Awaitable
-
Components:
await_ready: Checks if the coroutine is ready to proceed without suspending.await_suspend: Decides if the coroutine should suspend.await_resume: Executes upon resumption of the coroutine.
-
Behavior Determination:
- These functions are integral to managing the coroutine's state transitions from active to suspended and vice versa.
Implementation and Execution Flow
- Compiler Interaction:
- The compiler transforms coroutine syntax into a sequence of operations involving the Awaitable's methods.
await_ready: Evaluated first; if it returns false,await_suspendis invoked.await_suspend: Determines the next state—either continuing execution or suspending.await_resume: Called upon resumption, handling tasks specific to resuming the coroutine's operation.
Built-in Awaitables
-
Types:
suspend_always: Forces the coroutine to suspend every time it's encountered.suspend_never: Ensures the coroutine never suspends at the specified point.
-
Usage:
- Chosen based on the required synchronization behavior of the coroutine with other operations or coroutines.
Advanced Awaitable Patterns
- Symmetric Coroutine Transfer:
- An advanced use case where
await_suspendcan return a coroutine handle, allowing direct transfer of execution to another coroutine without returning to the caller.
- An advanced use case where
Understanding await_suspend return type variants
- Void: Always suspends the coroutine.
- Boolean: Decides to suspend based on a true/false value.
- Coroutine Handle: Enables direct switching to another coroutine.
couroutine_handle: just like a pointer to the coroutine

-
A
coroutine_handleis akin to a pointer specifically designed for managing coroutines in C++. It provides the functionality necessary to control and interact with a coroutine's lifecycle and state. -
Key Features of Coroutine Handles
-
Accessing Promise:
- The
promise()function allows accessing the promise object associated with the coroutine, which manages the coroutine’s state and interaction with its lifecycle events.
- The
-
Constructing from Promise:
- The static method
from_promise()enables constructing acoroutine_handlefrom a promise object. This establishes a direct link between the handle and the coroutine’s operational context.
- The static method
-
Memory Address Access:
address()provides the raw pointer address of the coroutine, reminiscent of traditional C-pointer operations, allowing for low-level manipulations if necessary.
-
Reconstructing from Address:
- Conversely,
from_address()allows reconstructing acoroutine_handlefrom a raw pointer, facilitating operations that might involve lower-level system integrations.
- Conversely,
-
Template Specialization
- Generalization via
coroutine_handle<void>:- This template specialization allows a
coroutine_handleto be generalized for any coroutine regardless of its promise type. It’s particularly useful for managing coroutines in a type-agnostic manner. \
- This template specialization allows a
- Limitation of Generalized Handles:
- With
coroutine_handle<void>, you lose the ability to directly access the promise throughhandle.promise()because the specific type of the promise is unknown to the compiler. This requires more generic handling of coroutine states.
- With
Lifecycle Management Functions
-
resume():- Resumes the execution of a suspended coroutine, allowing it to continue from where it left off.
-
done():- Checks whether the coroutine has completed its execution. This function is critical for managing the flow of control in asynchronous programming to ensure that operations dependent on the coroutine’s completion are handled appropriately.
-
destroy():- Explicitly destroys the coroutine handle, which is necessary to release resources associated with the coroutine, especially when its lifecycle needs to be managed manually to prevent leaks or other issues.
(Skip remaining code discussion. Already put to repo)