Vix.cpp v2.8.5 is here Read the blog
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.