Async
vix::async provides asynchronous execution primitives for Vix applications.
Use it when you need coroutine tasks, an async runtime context, timers, cancellation, background work, signals, or async networking.
Public header
#include <vix/async.hpp>
#include <vix/print.hpp>2
What Async provides
Vix Async provides a small coroutine-based runtime.
It includes:
io_contextfor runtime coordinationtask<T>andtask<void>for coroutine workspawnfor starting taskstimerfor delayed executioncancel_sourceandcancel_tokenfor cooperative cancellationthread_poolfor CPU-heavy or blocking workwhen_allandwhen_anyfor task compositionsignal_setfor async signal handling- TCP, UDP, and DNS helpers backed by the async runtime
Basic idea
Most async programs use an io_context.
vix::async::core::io_context ctx;The context owns the scheduler and lazily creates services when needed:
ctx.timers();
ctx.cpu_pool();
ctx.signals();
ctx.net();2
3
4
Then the context runs the scheduler loop:
ctx.run();Stop it when the async work is done:
ctx.stop();Minimal async program
#include <vix/async.hpp>
#include <vix/print.hpp>
int main()
{
vix::async::core::io_context ctx;
ctx.post([&ctx]()
{
vix::print("hello from async");
ctx.stop();
});
ctx.run();
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Run:
vix run main.cppExpected output:
hello from asyncCoroutine task
A task<T> represents async work that can produce a value.
#include <vix/async.hpp>
#include <vix/print.hpp>
vix::async::core::task<int> compute()
{
co_return 42;
}
vix::async::core::task<void> app(vix::async::core::io_context &ctx)
{
const int value = co_await compute();
vix::print("value =", value);
ctx.stop();
co_return;
}
int main()
{
vix::async::core::io_context ctx;
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());
ctx.run();
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
Expected output:
value = 42Timers
Use timers when a coroutine needs to wait.
#include <vix/async.hpp>
#include <vix/print.hpp>
#include <chrono>
using namespace std::chrono_literals;
vix::async::core::task<void> app(vix::async::core::io_context &ctx)
{
vix::print("before sleep");
co_await ctx.timers().sleep_for(100ms);
vix::print("after sleep");
ctx.stop();
co_return;
}
int main()
{
vix::async::core::io_context ctx;
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());
ctx.run();
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
Expected output:
before sleep
after sleep2
Cancellation
Use cancel_source to request cancellation and cancel_token to observe it.
#include <vix/async.hpp>
#include <vix/print.hpp>
int main()
{
vix::async::core::cancel_source source;
vix::async::core::cancel_token token = source.token();
vix::print("can cancel =", token.can_cancel() ? "yes" : "no");
source.request_cancel();
vix::print("cancelled =", token.is_cancelled() ? "yes" : "no");
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Expected output:
can cancel = yes
cancelled = yes2
Thread pool
Use the CPU pool when a coroutine needs to offload blocking or CPU-heavy work.
#include <vix/async.hpp>
#include <vix/print.hpp>
vix::async::core::task<void> app(vix::async::core::io_context &ctx)
{
int value = co_await ctx.cpu_pool().submit([]()
{
return 21 * 2;
});
vix::print("value =", value);
ctx.stop();
co_return;
}
int main()
{
vix::async::core::io_context ctx;
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());
ctx.run();
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
Expected output:
value = 42Network helpers
The async module also includes networking APIs.
| API | Purpose |
|---|---|
dns_resolver | Resolve hostnames asynchronously. |
tcp_stream | Connect, read, and write TCP streams. |
tcp_listener | Listen and accept TCP connections. |
udp_socket | Bind, send, and receive UDP datagrams. |
The network backend is attached to io_context and is created lazily when networking is used.
Core components
| Component | Purpose |
|---|---|
io_context | Owns the scheduler and async services. |
scheduler | Runs callbacks and resumes coroutines. |
task<T> | Coroutine task that produces a value. |
task<void> | Coroutine task with no returned value. |
timer | Runs callbacks later or sleeps a coroutine. |
cancel_source | Requests cancellation. |
cancel_token | Observes cancellation. |
thread_pool | Runs work outside the scheduler thread. |
signal_set | Waits for system signals. |
Typical flow
create io_context
create or spawn async work
run the context
async work completes
stop the context2
3
4
5
In code:
vix::async::core::io_context ctx;
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());
ctx.run();2
3
4
5
6
Common workflows
Run a posted callback
vix::async::core::io_context ctx;
ctx.post([&ctx]()
{
vix::print("callback");
ctx.stop();
});
ctx.run();2
3
4
5
6
7
8
9
Run a coroutine task
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());
ctx.run();2
3
4
Sleep inside a coroutine
co_await ctx.timers().sleep_for(std::chrono::milliseconds(100));Offload CPU work
int value = co_await ctx.cpu_pool().submit([]()
{
return 42;
});2
3
4
Request cancellation
vix::async::core::cancel_source source;
source.request_cancel();2
3
Common mistakes
Forgetting to run the context
Wrong:
vix::async::core::io_context ctx;
ctx.post([]()
{
vix::print("hello");
});2
3
4
5
6
Correct:
vix::async::core::io_context ctx;
ctx.post([&ctx]()
{
vix::print("hello");
ctx.stop();
});
ctx.run();2
3
4
5
6
7
8
9
Forgetting to stop the context
If no code calls stop(), run() may keep waiting for work.
ctx.post([&ctx]()
{
vix::print("done");
ctx.stop();
});2
3
4
5
Destroying a task before starting it
task<T> is lazy. Create it, then start it or await it.
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());2
Using blocking work on the scheduler thread
Avoid running heavy blocking work directly inside the scheduler. Use the CPU pool:
auto result = co_await ctx.cpu_pool().submit([]()
{
return expensive_work();
});2
3
4
Ignoring cancellation
When an API accepts a cancel_token, pass one if the operation should be cancellable.
vix::async::core::cancel_source source;
co_await ctx.timers().sleep_for(1s, source.token());2
3
Best practices
Use one io_context as the owner of async services. Use task<T> for coroutine-based async work. Use timers for delays instead of blocking sleeps. Use the CPU pool for blocking or CPU-heavy functions. Use cancellation tokens for long-running operations. Call ctx.stop() when the main async work is complete. Call ctx.shutdown() when you want to explicitly release services.
Related pages
| Page | Purpose |
|---|---|
| io_context | Learn the runtime context and scheduler loop. |
| Tasks | Learn task<T> and task<void>. |
| Spawn | Learn how to start detached tasks. |
| Timers | Learn delays and timer callbacks. |
| Cancellation | Learn cancel_source and cancel_token. |
| Thread pool | Learn background work. |
| when_all / when_any | Learn task composition. |
| Signals | Learn async signal handling. |
| TCP | Learn TCP streams and listeners. |
| UDP | Learn UDP sockets. |
| DNS | Learn async DNS resolution. |
| API Reference | See the public API surface. |
Next step
Continue with io_context.