Appearance
Async
The vix::async module provides a coroutine-based asynchronous runtime.
This guide explains the core building blocks and how they fit together. It focuses on patterns, not full showcase applications.
Core Concepts
Main components:
io_context→ event looptask<T>→ coroutine tasktimers()→ async sleepcpu_pool()→ offload CPU worksignals()→ signal handlingspawn_detached()→ fire-and-forget coroutine
Async in Vix is:
- Explicit
- Coroutine-based
- No hidden threads
- Fully controlled by your event loop
1. Event loop and task
Every async program starts with an io_context.
Minimal structure:
cpp
#include <vix/async/core/io_context.hpp>
#include <vix/async/core/task.hpp>
using vix::async::core::io_context;
using vix::async::core::task;
task<void> app(io_context& ctx)
{
// async logic here
ctx.stop();
co_return;
}
int main()
{
io_context ctx;
auto t = app(ctx);
ctx.post(t.handle());
ctx.run();
}Flow:
- Create
io_context - Create a
task - Post the coroutine handle
- Call
run()
Nothing runs automatically. You control the loop.
2. Timers
Use ctx.timers().sleep_for() for async delays.
cpp
co_await ctx.timers().sleep_for(
std::chrono::milliseconds(50)
);This does not block the thread. The coroutine suspends and resumes later.
3. CPU pool
Heavy CPU work should not block the event loop.
Use:
cpp
int result = co_await ctx.cpu_pool().submit([]{
int sum = 0;
for (int i = 0; i < 100000; ++i)
sum += (i % 7);
return sum;
});This runs the lambda on a worker thread pool. The coroutine resumes when the result is ready.
Rule:
- I/O and coordination stay in the event loop.
- CPU-heavy work goes to
cpu_pool().
4. Signals
To handle SIGINT or SIGTERM:
cpp
auto& sig = ctx.signals();
sig.add(SIGINT);
sig.add(SIGTERM);
sig.on_signal([&](int s){
ctx.stop();
});
co_await sig.async_wait();This allows clean shutdown logic.
5. TCP server pattern
Async servers follow this structure:
- Create listener
async_listen- Loop with
async_accept - Spawn per-connection task
Pattern:
cpp
auto listener = make_tcp_listener(ctx);
co_await listener->async_listen({"0.0.0.0", 9090}, 128);
while (ctx.is_running())
{
auto client = co_await listener->async_accept();
spawn_detached(
ctx,
handle_client(std::move(client))
);
}Each client is handled in its own coroutine. No hidden threads are created.
6. when_all and when_any
Run multiple tasks concurrently.
cpp
auto tup = co_await when_all(sched, a(), b());- Waits for all tasks
- Returns a tuple of results
cpp
auto [index, values] =
co_await when_any(sched, a(), b());- Returns when the first task completes
indextells which one finished
Useful for parallel I/O or timeouts.
Execution Model
Vix async is:
- Single explicit event loop
- Cooperative scheduling
- Coroutine suspension via
co_await - Deterministic control over lifecycle
You decide:
- When to stop
- Where CPU work runs
- How tasks are composed
Recommended Usage
- Use
io_contextas your runtime root - Offload heavy work to
cpu_pool() - Always stop the context explicitly
- Prefer structured concurrency (
when_all,when_any) - Use
spawn_detachedcarefully (fire-and-forget)
Async in Vix follows the same philosophy as the rest of the runtime:
Explicit control. No hidden magic.