|
VLink
2.0.0
A high-performance communication middleware
|
Stackless C++20 coroutine layer that resumes work onto MessageLoop threads.
More...
#include "../version.h"Go to the source code of this file.
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.
* +----------+ 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 * +-----------+ *
| 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 |
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.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.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.await_future shares this thread; latency is bounded by the cadence plus the target loop's task dispatch delay.kPriorityType loops the post helper remaps MessageLoop::kNoPriority to MessageLoop::kNormalPriority so default-priority resumes do not sink to the queue tail.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.[&] and [=]) becomes a dangling reference. Always thread captures through function parameters so the compiler copies them into the coroutine frame.