From 57418ec714f16f738e8fbfcdeaece6feb603810d Mon Sep 17 00:00:00 2001 From: Michael Vandeberg Date: Fri, 19 Jun 2026 11:57:36 -0600 Subject: [PATCH] docs: document delay as asynchronous sleep (#295) Add an "Asynchronous Sleep" section to the concurrent composition page that frames delay as the non-blocking counterpart to sleep_for. Cross-link the custom-IoAwaitable guide to delay_awaitable as a production example of the stop-callback-must-post pattern, and list delay.hpp/timeout.hpp in the page reference table. --- .../pages/4.coroutines/4d.io-awaitable.adoc | 2 ++ .../pages/4.coroutines/4f.composition.adoc | 30 ++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/doc/modules/ROOT/pages/4.coroutines/4d.io-awaitable.adoc b/doc/modules/ROOT/pages/4.coroutines/4d.io-awaitable.adoc index 80b6f2df9..48672d51d 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4d.io-awaitable.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4d.io-awaitable.adoc @@ -215,6 +215,8 @@ stop_cb_.emplace(env->stop_token, h); // h is a raw coroutine_handle See xref:4.coroutines/4e.cancellation.adoc#stoppable-awaitables[Implementing Stoppable Awaitables] for a complete example. +For a production implementation of this exact pattern, read the source of `delay_awaitable` (xref:reference:boost/capy/delay_awaitable.adoc[`delay_awaitable`]): it schedules a timer, registers a stop callback that posts the resume through the executor, and arbitrates between the timer and cancellation with a single atomic claim. + == Reference [cols="1,3"] diff --git a/doc/modules/ROOT/pages/4.coroutines/4f.composition.adoc b/doc/modules/ROOT/pages/4.coroutines/4f.composition.adoc index d4750b19b..eddf23886 100644 --- a/doc/modules/ROOT/pages/4.coroutines/4f.composition.adoc +++ b/doc/modules/ROOT/pages/4.coroutines/4f.composition.adoc @@ -228,9 +228,31 @@ task process_all(std::vector const& items) } ---- +=== Asynchronous Sleep + +`delay` is the awaitable counterpart to `std::this_thread::sleep_for`. Instead of blocking the thread, it suspends the current coroutine until the duration elapses, leaving the thread free to run other coroutines in the meantime: + +[source,cpp] +---- +#include + +task<> example() +{ + auto [ec] = co_await delay(100ms); + // 100ms have elapsed; other coroutines ran on this thread while we waited +} +---- + +[NOTE] +==== +A thread is *not* consumed per sleeping coroutine. All concurrently sleeping coroutines on the same execution context share a single timer thread, so a thousand simultaneous `delay()` calls cost one thread, not a thousand. +==== + +`delay` is cancellable. If the environment's stop token is activated before the deadline, the coroutine resumes early with `ec` set to `error::canceled` (compare with `cond::canceled`); otherwise `ec` is clear. A zero or negative duration completes synchronously without scheduling a timer. + === Timeout -The `timeout` combinator races an awaitable against a deadline: +The `timeout` combinator races an awaitable against a deadline. It is built directly on `delay` — the inner awaitable is run against a `delay` of the given duration, and whichever completes first cancels the other: [source,cpp] ---- @@ -281,6 +303,12 @@ This design ensures proper context propagation to all children. | `` | First-completion racing with when_any +| `` +| Asynchronous sleep that suspends instead of blocking the thread + +| `` +| Race an awaitable against a deadline + | `` | Race an awaitable against a deadline |===