Skip to content

API Reference

This page is a compact reference for the public Vix Async API.

Use it when you want to quickly find the main types, functions, and operations exposed by vix::async.

Public header

cpp
#include <vix/async.hpp>
#include <vix/print.hpp>

Namespaces

Core async APIs live in:

vix::async::core

Networking APIs live in:

vix::async::net

Main components

ComponentPurpose
io_contextCentral async runtime context.
schedulerRuns callbacks and resumes coroutines.
task<T>Coroutine task producing a value.
task<void>Coroutine task with no returned value.
cancel_sourceOwns cancellation state.
cancel_tokenObserves cancellation state.
timerDelayed callbacks and coroutine sleeps.
thread_poolBackground worker pool for CPU or blocking work.
signal_setAsync signal watching.
when_allWaits for multiple tasks to complete.
when_anyWaits for the first task to complete.
dns_resolverAsync hostname resolution.
tcp_streamConnected TCP stream.
tcp_listenerTCP server listener.
udp_socketUDP datagram socket.

io_context

io_context is the central runtime object.

cpp
vix::async::core::io_context ctx;

It owns the scheduler and async services.

post

Posts a callback to the scheduler.

cpp
ctx.post([&ctx]()
{
  vix::print("hello");
  ctx.stop();
});

run

Runs the scheduler loop. Blocks until the context is stopped.

cpp
ctx.run();

stop

Requests the scheduler loop to stop.

cpp
ctx.stop();

is_running

Checks whether the scheduler is running.

cpp
vix::print("running =", ctx.is_running() ? "yes" : "no");

get_scheduler

Returns the internal scheduler.

cpp
auto &sched = ctx.get_scheduler();

Use it when starting tasks.

cpp
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());

cpu_pool

Returns the CPU thread pool.

cpp
auto &pool = ctx.cpu_pool();

Use it to run blocking or CPU-heavy work.

timers

Returns the timer service.

cpp
auto &timers = ctx.timers();

Use it for delayed callbacks and coroutine sleeps.

signals

Returns the signal service.

cpp
auto &signals = ctx.signals();

Use it for async signal handling.

net

Returns the internal networking backend.

cpp
ctx.net();

Most user code should prefer the higher-level networking factories:

cpp
vix::async::net::make_dns_resolver(ctx);
vix::async::net::make_tcp_stream(ctx);
vix::async::net::make_tcp_listener(ctx);
vix::async::net::make_udp_socket(ctx);

shutdown

Stops the scheduler and releases services.

cpp
ctx.shutdown();

The destructor also shuts down the context.

scheduler

scheduler runs callbacks and coroutine continuations.

Most application code uses it through io_context.

cpp
auto &sched = ctx.get_scheduler();

post

Posts a callable.

cpp
sched.post([]()
{
  vix::print("callback");
});

post_handle

Posts a coroutine handle. Most user code does not call this directly.

cpp
sched.post_handle(handle);

schedule

Creates an awaitable that resumes the coroutine on the scheduler.

cpp
co_await ctx.get_scheduler().schedule();

run

Runs the scheduler loop. In normal applications, prefer ctx.run().

stop

Stops the scheduler. In normal applications, prefer ctx.stop().

task

task<T> is the main coroutine task type.

task<T>

Use task<T> when the coroutine returns a value.

cpp
vix::async::core::task<int> compute()
{
  co_return 42;
}

Await it from another task:

cpp
const int value = co_await compute();

task<void>

Use task<void> when the coroutine returns no value.

cpp
vix::async::core::task<void> work()
{
  vix::print("working");
  co_return;
}

start

Starts a task on a scheduler. Tasks are lazy — they do not run until awaited or started.

cpp
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());

valid

Checks whether a task owns a coroutine handle.

cpp
if (t.valid())
{
  vix::print("valid task");
}

Equivalent:

cpp
if (t)
{
  vix::print("valid task");
}

release

Releases the underlying coroutine handle. Most user code should prefer start.

cpp
auto handle = t.release();

Minimal task example

cpp
#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;
}

Cancellation

Vix Async cancellation is cooperative.

cancel_source

Owns the cancellation state.

cpp
vix::async::core::cancel_source source;

Request cancellation:

cpp
source.request_cancel();

Check source state:

cpp
if (source.is_cancelled())
{
  vix::print("cancelled");
}

cancel_token

Observes cancellation state.

cpp
vix::async::core::cancel_token token = source.token();

Check whether the token is connected to a source:

cpp
if (token.can_cancel())
{
  vix::print("can cancel");
}

Check whether cancellation was requested:

cpp
if (token.is_cancelled())
{
  vix::print("cancel requested");
}

cancelled_ec

Returns the standard async cancellation error code.

cpp
const std::error_code ec = vix::async::core::cancelled_ec();

vix::print("message =", ec.message());

Timer

timer provides delayed callbacks and coroutine sleeps.

Access it through io_context.

cpp
ctx.timers()

sleep_for

Suspends a coroutine for a duration.

cpp
co_await ctx.timers().sleep_for(std::chrono::milliseconds(100));

With cancellation:

cpp
co_await ctx.timers().sleep_for(
    std::chrono::seconds(1),
    source.token());

after

Schedules a callback after a duration.

cpp
ctx.timers().after(std::chrono::milliseconds(100), []()
{
  vix::print("timer fired");
});

With cancellation:

cpp
ctx.timers().after(
    std::chrono::seconds(1),
    []()
    {
      vix::print("callback");
    },
    source.token());

Timer example

cpp
#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;
}

Thread pool

thread_pool runs CPU-heavy or blocking work outside the scheduler thread.

Access it through io_context.

cpp
ctx.cpu_pool()

submit

Submits a plain callback.

cpp
ctx.cpu_pool().submit([]()
{
  do_work();
});

Awaitable submit

Submits a callable and awaits the result.

cpp
int value = co_await ctx.cpu_pool().submit([]()
{
  return 42;
});

With cancellation:

cpp
int value = co_await ctx.cpu_pool().submit([]()
{
  return 42;
}, source.token());

Thread pool example

cpp
#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;
}

when_all

when_all starts multiple tasks and waits until all of them complete.

cpp
auto results = co_await vix::async::core::when_all(
    ctx.get_scheduler(),
    first(),
    second());

Results are returned as a tuple.

cpp
const auto a = std::get<0>(results);
const auto b = std::get<1>(results);

Example:

cpp
#include <vix/async.hpp>
#include <vix/print.hpp>

#include <tuple>

vix::async::core::task<int> first()
{
  co_return 20;
}

vix::async::core::task<int> second()
{
  co_return 22;
}

vix::async::core::task<void> app(vix::async::core::io_context &ctx)
{
  auto results = co_await vix::async::core::when_all(
      ctx.get_scheduler(),
      first(),
      second());

  vix::print("sum =", std::get<0>(results) + std::get<1>(results));

  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;
}

when_any

when_any starts multiple tasks and waits for the first one to complete.

cpp
auto result = co_await vix::async::core::when_any(
    ctx.get_scheduler(),
    fast(),
    slow());

The result contains:

FieldPurpose
result.firstIndex of the first completed task.
result.secondTuple of optional result slots.

Example:

cpp
const std::size_t index = result.first;
auto values = std::move(result.second);

signal_set

signal_set watches system signals.

Access it through io_context.

cpp
ctx.signals()

add

Adds a signal to watch.

cpp
ctx.signals().add(SIGINT);

remove

Removes a watched signal.

cpp
ctx.signals().remove(SIGINT);

async_wait

Waits for the next signal.

cpp
const int sig = co_await ctx.signals().async_wait();

With cancellation:

cpp
const int sig = co_await ctx.signals().async_wait(source.token());

on_signal

Registers a callback.

cpp
ctx.signals().on_signal([](int sig)
{
  vix::print("signal =", sig);
});

stop

Stops signal watching.

cpp
ctx.signals().stop();

Signal example

cpp
#include <vix/async.hpp>
#include <vix/print.hpp>

#include <csignal>

vix::async::core::task<void> app(vix::async::core::io_context &ctx)
{
  ctx.signals().add(SIGINT);

  vix::print("press Ctrl+C to stop");

  const int sig = co_await ctx.signals().async_wait();

  vix::print("signal =", sig);

  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;
}

DNS

DNS APIs live in vix::async::net.

resolved_address

Represents one resolved endpoint.

FieldPurpose
ipIP address as text.
portPort number.

make_dns_resolver

Creates a resolver attached to an io_context.

cpp
auto resolver = vix::async::net::make_dns_resolver(ctx);

async_resolve

Resolves a hostname and port.

cpp
auto addresses = co_await resolver->async_resolve(
    "example.com",
    80);

With cancellation:

cpp
auto addresses = co_await resolver->async_resolve(
    "example.com",
    80,
    source.token());

TCP

TCP APIs live in vix::async::net.

tcp_endpoint

Describes a TCP endpoint.

cpp
vix::async::net::tcp_endpoint endpoint{
    "127.0.0.1",
    8080
};
FieldPurpose
hostHostname or IP address.
portTCP port.

tcp_stream

Represents a connected TCP stream.

MethodPurpose
async_connect(endpoint, token)Connects to a remote endpoint.
async_read(buffer, token)Reads bytes.
async_write(buffer, token)Writes bytes.
close()Closes the stream.
is_open()Checks open state.
native_handle()Returns native socket handle when supported.

make_tcp_stream

Creates a TCP stream.

cpp
auto stream = vix::async::net::make_tcp_stream(ctx);

async_connect

Connects to a remote endpoint.

cpp
co_await stream->async_connect({
    "example.com",
    80
});

With cancellation:

cpp
co_await stream->async_connect(endpoint, source.token());

async_read

Reads bytes into a mutable byte buffer.

cpp
std::array<std::byte, 1024> buffer{};

const std::size_t bytes = co_await stream->async_read(
    std::span<std::byte>{buffer.data(), buffer.size()});

async_write

Writes bytes from an immutable byte buffer.

cpp
std::string message = "hello";

const std::size_t bytes = co_await stream->async_write(
    std::as_bytes(std::span{message}));

tcp_listener

Represents a listening TCP socket.

MethodPurpose
async_listen(endpoint, backlog)Starts listening.
async_accept(token)Accepts one connection.
close()Closes the listener.
is_open()Checks open state.

make_tcp_listener

Creates a TCP listener.

cpp
auto listener = vix::async::net::make_tcp_listener(ctx);

async_listen

Starts listening on a local endpoint.

cpp
co_await listener->async_listen({
    "127.0.0.1",
    8080
});

async_accept

Accepts one incoming connection. The returned client is a std::unique_ptr<tcp_stream>.

cpp
auto client = co_await listener->async_accept();

UDP

UDP APIs live in vix::async::net.

udp_endpoint

Describes a UDP endpoint.

cpp
vix::async::net::udp_endpoint endpoint{
    "127.0.0.1",
    9000
};
FieldPurpose
hostHostname or IP address.
portUDP port.

udp_datagram

Describes a received datagram.

FieldPurpose
fromSender endpoint.
bytesNumber of received bytes.

udp_socket

Represents a UDP socket.

MethodPurpose
async_bind(endpoint)Binds to a local endpoint.
async_send_to(buffer, endpoint, token)Sends one datagram.
async_recv_from(buffer, token)Receives one datagram.
close()Closes the socket.
is_open()Checks open state.

make_udp_socket

Creates a UDP socket.

cpp
auto socket = vix::async::net::make_udp_socket(ctx);

async_bind

Binds the socket to a local endpoint.

cpp
co_await socket->async_bind({
    "127.0.0.1",
    9000
});

async_send_to

Sends one datagram.

cpp
std::string message = "hello";

const std::size_t bytes = co_await socket->async_send_to(
    std::as_bytes(std::span{message}),
    {
        "127.0.0.1",
        9000
    });

async_recv_from

Receives one datagram.

cpp
std::array<std::byte, 1024> buffer{};

const auto datagram = co_await socket->async_recv_from(
    std::span<std::byte>{buffer.data(), buffer.size()});

Error codes

Async core errors use vix::async::core::errc.

ErrorMeaning
okNo error.
invalid_argumentInvalid argument.
not_readyOperation cannot complete yet.
timeoutOperation timed out.
canceledOperation was canceled.
closedResource was closed.
overflowCapacity or numeric overflow.
stoppedRuntime or scheduler was stopped.
queue_fullInternal task queue is full.
rejectedTask submission was rejected.
not_supportedOperation is not supported on this platform.

make_error_code

Creates a std::error_code from an async error.

cpp
std::error_code ec =
    vix::async::core::make_error_code(
        vix::async::core::errc::canceled);

Operation table

GoalAPI
Create runtimevix::async::core::io_context ctx
Post callbackctx.post(...)
Run schedulerctx.run()
Stop schedulerctx.stop()
Start taskstd::move(task).start(ctx.get_scheduler())
Sleep coroutinectx.timers().sleep_for(duration)
Delayed callbackctx.timers().after(duration, fn)
Create cancellation sourcecancel_source source
Get cancellation tokensource.token()
Request cancellationsource.request_cancel()
Offload workctx.cpu_pool().submit(fn)
Wait all taskswhen_all(ctx.get_scheduler(), ...)
Wait first taskwhen_any(ctx.get_scheduler(), ...)
Wait signalctx.signals().async_wait()
Resolve DNSresolver->async_resolve(host, port)
Connect TCPstream->async_connect(endpoint)
Read TCPstream->async_read(buffer)
Write TCPstream->async_write(buffer)
Listen TCPlistener->async_listen(endpoint)
Accept TCPlistener->async_accept()
Bind UDPsocket->async_bind(endpoint)
Send UDPsocket->async_send_to(buffer, endpoint)
Receive UDPsocket->async_recv_from(buffer)

Complete minimal runtime example

cpp
#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;
}

Complete coroutine example

cpp
#include <vix/async.hpp>
#include <vix/print.hpp>

#include <chrono>

using namespace std::chrono_literals;

vix::async::core::task<int> compute(
    vix::async::core::io_context &ctx)
{
  co_await ctx.timers().sleep_for(100ms);

  co_return 42;
}

vix::async::core::task<void> app(
    vix::async::core::io_context &ctx)
{
  const int value = co_await compute(ctx);

  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;
}

Complete cancellation example

cpp
#include <vix/async.hpp>
#include <vix/print.hpp>

#include <chrono>
#include <system_error>

using namespace std::chrono_literals;

vix::async::core::task<void> app(
    vix::async::core::io_context &ctx)
{
  vix::async::core::cancel_source source;

  ctx.timers().after(100ms, [&source]()
  {
    source.request_cancel();
  });

  try
  {
    co_await ctx.timers().sleep_for(1s, source.token());

    vix::print("sleep completed");
  }
  catch (const std::system_error &ex)
  {
    vix::eprint("cancelled:", ex.code().message());
  }

  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;
}

Common mistakes

Creating a task but never starting it

Wrong:

cpp
auto t = app(ctx);

ctx.run();

Correct:

cpp
auto t = app(ctx);
std::move(t).start(ctx.get_scheduler());

ctx.run();

Forgetting to run the context

Wrong:

cpp
ctx.post([]()
{
  vix::print("hello");
});

Correct:

cpp
ctx.post([&ctx]()
{
  vix::print("hello");
  ctx.stop();
});

ctx.run();

Forgetting to stop the context

If nothing calls ctx.stop(), the context may continue waiting.

cpp
ctx.stop();

Blocking the scheduler thread

Wrong:

cpp
std::this_thread::sleep_for(std::chrono::seconds(1));

Correct:

cpp
co_await ctx.timers().sleep_for(std::chrono::seconds(1));

For CPU-heavy work:

cpp
auto value = co_await ctx.cpu_pool().submit([]()
{
  return expensive_work();
});

Passing text directly to network write APIs

Network write APIs expect byte spans.

Wrong:

cpp
co_await stream->async_write(message);

Correct:

cpp
co_await stream->async_write(
    std::as_bytes(std::span{message}));

Ignoring network errors

Network operations can throw.

cpp
try
{
  co_await stream->async_connect(endpoint);
}
catch (const std::exception &ex)
{
  vix::eprint(ex.what());
}

Reusing moved tasks

Wrong:

cpp
auto t = app(ctx);

std::move(t).start(ctx.get_scheduler());
std::move(t).start(ctx.get_scheduler());

Correct:

cpp
auto t = app(ctx);

std::move(t).start(ctx.get_scheduler());
PagePurpose
OverviewStart with the async overview.
io_contextLearn the runtime context.
TasksLearn coroutine tasks.
SpawnLearn how to start tasks.
TimersLearn timer sleeps and callbacks.
CancellationLearn cooperative cancellation.
Thread poolLearn background work.
when_all / when_anyLearn task composition.
SignalsLearn signal handling.
TCPLearn TCP streams and listeners.
UDPLearn UDP sockets.
DNSLearn DNS resolution.

Released under the MIT License.