VLink  2.0.0
A high-performance communication middleware
coroutine.h 文件参考

Stackless C++20 coroutine layer that resumes work onto MessageLoop threads. 更多...

#include "../version.h"
coroutine.h 的引用(Include)关系图:

浏览源代码.

详细描述

Stackless C++20 coroutine layer that resumes work onto MessageLoop threads.

Adds an additive coroutine binding atop MessageLoop, GraphTask and Schedule. Every awaiter eventually re-enters the target loop through MessageLoop::post_task or MessageLoop::exec_task; the loop itself is unchanged. The layer compiles in only when VLINK_ENABLE_COROUTINE is defined (auto-enabled when VLINK_ENABLE_CXX_STD_20 is set and the toolchain advertises __cpp_impl_coroutine and __cpp_lib_coroutine); otherwise the file is empty. The canonical namespace is vlink::Coroutine with vlink::Co as a short alias.

State machine of a coroutine resume
*   +----------+    co_spawn /    +-----------+   awaiter   +-------------+
*   | created  | -- co_await ---> | suspended | -- ready -> | resumed on  |
*   +----------+                  +-----------+             | target loop |
*        |                             ^                    +-------------+
*        |                             |                          |
*        |    queue full (kRetry)      |                          | co_return
*        |  +--------------------------+                          v
*        |  |                                              +-------------+
*        |  | helper thread retries every ~1ms             |  finished   |
*        v  v                                              +-------------+
*   +-----------+    loop closed (kClosed)
*   |  failed   |  --> await_resume throws runtime_error / OperationCancelled
*   +-----------+
* 
Awaitable surface
Category Symbol Purpose
Core type Task<TypeT> Lazily started awaitable
Awaiter schedule(loop, prio) Hop onto loop 's thread
Awaiter yield(loop, prio) Re-post to the back of the queue
Awaiter delay_ms(loop, ms, prio) Non-blocking sleep
Awaiter await_future(loop, fut) Wait on std::future
Entry point co_spawn(loop, task) Fire-and-forget Task<void>
Entry point co_spawn(loop, task, on_done) Spawn with completion callback
Entry point co_spawn_with_priority(...) Same overloads with priority
Bridge exec(loop, config, fn) Wrap MessageLoop::exec_task
Bridge await_graph(loop, graph) Wait on GraphTask DAG
Orchestration when_all(loop, tasks) Join all, collect results
Orchestration when_any(loop, tasks) First success and its index
Orchestration sequence(loop, tasks) Run in order
Frame allocation
Coroutine frames flow through vlink::MemoryPool::global_instance() via the operator new / operator delete overloads on TaskPromise and DetachedTask::promise_type. The allocator throws std::bad_alloc on pool exhaustion; there is no custom-allocator template parameter.
Lock order
MessageLoop::AliveState::mtx -> MessageLoop::Impl::mtx -> TaskHandle::State::mtx -> awaiter shared-state mutex. Each awaiter owns its own state object that holds the suspended coroutine handle. When the target loop closes mid-resume the handle is released on the FutureWaitLoop helper thread via a queued retry; callers must therefore not assume await_resume runs on the original loop.
Cancellation contract
  • await_future rethrows the promise's stored exception via future.get(). When the target loop closes before the ready future can be posted back, await_resume throws vlink::Exception::OperationCancelled.
  • await_graph normally returns void; on the late-closure path it throws vlink::Exception::OperationCancelled.
  • schedule / yield / delay_ms retry posting through the FutureWaitLoop helper on kRetry; once detail::kMaxResumePostRetry (30000 ticks of approximately one millisecond each) is exhausted, or the loop reports kClosed, await_resume throws std::runtime_error with a descriptive message.
FutureWaitLoop
A process-wide helper thread polls every registered awaiter at a roughly one-millisecond cadence. Concurrent use of await_future shares this thread; latency is bounded by the cadence plus the target loop's task dispatch delay.
Priority remap on full-queue loops
On kPriorityType loops the post helper remaps MessageLoop::kNoPriority to MessageLoop::kNormalPriority so default-priority resumes do not sink to the queue tail.
Alive-state mutex hold time
post_callback_if_alive acquires MessageLoop::AliveState::mtx only briefly; the inner post_callback path uses TaskOverflowPolicy::kReject so the alive-state mutex is never held across a sleep or backoff.
Example
loop.async_run();
vlink::Co::Task<> my_routine(vlink::MessageLoop& loop, int* counter) {
co_await vlink::Co::delay_ms(loop, 100);
++(*counter);
co_await vlink::Co::yield(loop);
++(*counter);
}
int counter = 0;
vlink::Co::co_spawn(loop, my_routine(loop, &counter));
警告
Passing a coroutine lambda as a temporary, for example
co_spawn(loop, []() -> Task<> { co_await ... ; }());
destroys the lambda object at the end of the full-expression – long before the coroutine body runs. Any captured state (including [&] and [=]) becomes a dangling reference. Always thread captures through function parameters so the compiler copies them into the coroutine frame.