Vix.cpp v2.8.5 is here Read the blog
Skip to content

API Reference ​

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

For most application code, include:

cpp
#include <vix/async.hpp>

The module is divided into two main public namespaces:

cpp
vix::async::core
vix::async::net

Version information lives directly in:

cpp
vix::async

Types under a detail namespace are implementation details and should not be used as application API.

Core namespace ​

Most runtime types live in:

cpp
namespace vix::async::core

Common application code can use:

cpp
using namespace vix::async::core;

The main core API consists of:

text
task<T>
scheduler
io_context

cancel_source
cancel_token
cancel_registration

timer
thread_pool
signal_set

spawn_detached()
when_all()
when_any()

errc

task<T> ​

cpp
template <typename T>
class task;

task<T> represents a lazy coroutine computation that eventually produces T or throws an exception.

T must not be a reference type.

Use a wrapper such as:

cpp
std::reference_wrapper<T>

when reference semantics are required.

Construction ​

cpp
task() noexcept;

Tasks returned from coroutine functions are constructed by the coroutine machinery.

Application code normally does not construct a task from a coroutine handle directly.

Ownership ​

task<T> is move-only:

cpp
task(task&&) noexcept;

task& operator=(task&&) noexcept;

task(const task&) = delete;

task& operator=(const task&) = delete;

State ​

cpp
bool valid() const noexcept;

explicit operator bool() const noexcept;

Awaiting ​

cpp
auto operator co_await() & noexcept;

auto operator co_await() && noexcept;

The task begins when awaited.

For task<T>, co_await produces T.

For task<void>, it produces no value.

Captured exceptions are rethrown at the await boundary.

Coroutine handle ​

cpp
handle_type handle() const noexcept;

handle_type release() noexcept;

These operations expose low-level coroutine ownership.

Most application code does not need them.

Explicit start ​

cpp
void start(scheduler& sched) && noexcept;

start():

  • posts the coroutine to the scheduler
  • marks the task detached
  • releases ownership from the task object

Typical root-task use:

cpp
std::move(run(ctx)).start(
  ctx.get_scheduler()
);

See Tasks.

task<void> ​

cpp
template <>
class task<void>;

task<void> follows the same ownership, suspension, exception, and start rules as task<T>, but has no return value.

Typical form:

cpp
task<void> work()
{
  co_return;
}

scheduler ​

cpp
class scheduler;

The scheduler executes ready coroutine handles and callbacks on the thread running run().

It is non-copyable.

Post a callback ​

cpp
template <typename Fn>
void post(Fn&& fn);

Queues an ordinary callable.

Post a coroutine ​

cpp
void post(
  std::coroutine_handle<> handle
) noexcept;

Explicit fast-path alias:

cpp
void post_handle(
  std::coroutine_handle<> handle
) noexcept;

Schedule the current coroutine ​

cpp
schedule_awaitable schedule() noexcept;

Usage:

cpp
co_await sched.schedule();

The coroutine is posted to the scheduler and later resumes on the scheduler's run() thread.

Run ​

cpp
void run();

Runs the scheduler event loop on the calling thread.

Coroutine handles are processed before generic callback work.

Stop ​

cpp
void stop() noexcept;

Requests stop.

Already queued work is drained before run() exits.

Reset ​

cpp
void reset() noexcept;

Clears the scheduler stop request.

It must not be used while run() is active.

State ​

cpp
bool is_running() const noexcept;

bool stop_requested() const noexcept;

See Scheduler.

io_context ​

cpp
class io_context;

io_context owns the core scheduler and lazily creates Async services.

It is non-copyable.

Scheduler ​

cpp
scheduler& get_scheduler() noexcept;

const scheduler&
get_scheduler() const noexcept;

Post callbacks ​

cpp
template <typename Fn>
void post(Fn&& fn);

Post coroutine handles ​

cpp
void post(
  std::coroutine_handle<> handle
) noexcept;

void post_handle(
  std::coroutine_handle<> handle
) noexcept;

Run ​

cpp
void run();

Equivalent to driving the context scheduler.

Stop ​

cpp
void stop() noexcept;

Requests scheduler stop.

Running state ​

cpp
bool is_running() const noexcept;

CPU service ​

cpp
thread_pool& cpu_pool();

Created lazily.

Throws std::runtime_error when accessed after context shutdown.

Timer service ​

cpp
timer& timers();

Created lazily.

Throws std::runtime_error when accessed after context shutdown.

Signal service ​

cpp
signal_set& signals();

Created lazily.

Throws std::runtime_error when accessed after context shutdown.

Networking ​

Public networking objects should normally be created through:

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

The context also exposes its backend internally for integration, but application code should not depend on vix::async::net::detail.

Shutdown ​

cpp
void shutdown() noexcept;

Shuts down lazily created services while the scheduler can still accept their final completions, then stops the scheduler.

shutdown() is idempotent.

The destructor performs shutdown automatically.

See io_context and Lifecycle and Shutdown.

Cancellation ​

Cancellation is cooperative.

The primary public types are:

cpp
cancel_source
cancel_token
cancel_registration

cancel_source ​

cpp
class cancel_source;

Create a cancellation state:

cpp
cancel_source source;

Get a token:

cpp
cancel_token token() const noexcept;

Request cancellation:

cpp
void request_cancel() noexcept;

Inspect state:

cpp
bool is_cancelled() const noexcept;

cancel_token ​

cpp
class cancel_token;

An empty token is valid and represents no cancellation source.

cpp
cancel_token();

Inspect whether cancellation is available:

cpp
bool can_cancel() const noexcept;

Inspect cancellation state:

cpp
bool is_cancelled() const noexcept;

Register a callback:

cpp
cancel_registration on_cancel(
  std::function<void()> fn
) const;

If cancellation was already requested, the callback runs during on_cancel().

cancel_registration ​

cpp
class cancel_registration;

The registration is move-only.

Remove the callback registration:

cpp
void reset() noexcept;

Inspect its state:

cpp
bool active() const noexcept;

Destroying the registration also removes an active callback registration.

Cancellation error ​

cpp
std::error_code
cancelled_ec() noexcept;

Equivalent to:

cpp
make_error_code(
  errc::canceled
);

See Cancellation.

timer ​

cpp
class timer;

The timer service uses:

cpp
using clock =
  std::chrono::steady_clock;

using time_point =
  clock::time_point;

using duration =
  clock::duration;

Delayed callback ​

cpp
template <typename Fn>
void after(
  duration delay,
  Fn&& fn,
  cancel_token token = {}
);

Schedules a callback for later execution through the context scheduler.

Coroutine sleep ​

cpp
task<void> sleep_for(
  duration delay,
  cancel_token token = {}
);

Suspends the coroutine until:

  • the delay expires
  • cancellation occurs
  • the timer service stops

Stop ​

cpp
void stop() noexcept;

State ​

cpp
bool stopped() const noexcept;

See Timers.

thread_pool ​

cpp
class thread_pool;

The pool executes synchronous callables on worker threads.

Construction ​

cpp
explicit thread_pool(
  io_context& ctx,
  std::size_t threads =
    std::thread::hardware_concurrency()
);

A requested size of zero still produces at least one worker.

Most applications use:

cpp
ctx.cpu_pool();

instead of constructing a separate pool.

Fire-and-forget work ​

cpp
bool post(
  std::function<void()> fn
);

Returns true if accepted.

Returns false if the job cannot be accepted.

Submit and await ​

cpp
template <typename Fn>
auto submit(
  Fn&& fn,
  cancel_token token = {}
)
  -> task<
    std::invoke_result_t<
      std::decay_t<Fn>&
    >
  >;

The callable runs on a worker.

Its result or exception returns through the awaiting task.

Cancellation is checked before execution begins.

Stop ​

cpp
void stop() noexcept;

Rejects new work while accepted work drains.

Shutdown ​

cpp
void shutdown() noexcept;

Stops the pool and joins its worker threads.

The operation is idempotent.

State ​

cpp
bool stopped() const noexcept;

std::size_t size() const noexcept;

See Thread Pool and CPU Offloading.

signal_set ​

cpp
class signal_set;

Add a signal ​

cpp
void add(int signal);

Remove a signal ​

cpp
void remove(int signal);

Await the next signal ​

cpp
task<int> async_wait(
  cancel_token token = {}
);

Returns the received signal number.

Only one active waiter is supported at a time.

Observe signals with a callback ​

cpp
void on_signal(
  std::function<void(int)> fn
);

The callback is posted through the context scheduler.

Stop ​

cpp
void stop() noexcept;

Stops signal watching and releases an active waiter.

See Signals.

Detached tasks ​

Use:

cpp
void spawn_detached(
  io_context& ctx,
  task<void> task
);

to start a task<void> without retaining an awaitable result.

Example:

cpp
spawn_detached(
  ctx,
  background(ctx)
);

The detached coroutine self-destroys when complete.

Exceptions escaping the detached task are consumed at the detached boundary.

See Spawn and Detached Tasks.

when_all ​

cpp
template <typename... Ts>
task<
  std::tuple<
    std::conditional_t<
      std::is_void_v<Ts>,
      std::monostate,
      Ts
    >...
  >
>
when_all(
  scheduler& sched,
  task<Ts>... tasks
);

when_all:

  • starts all supplied tasks
  • waits for every task
  • preserves argument order in the result tuple
  • maps task<void> to std::monostate
  • accepts zero tasks
  • rethrows the first captured exception after all tasks finish

Example:

cpp
auto results = co_await when_all(
  ctx.get_scheduler(),
  first(),
  second()
);

See when_all and when_any.

when_any ​

The logical public result type is:

cpp
template <typename... Ts>
task<
  std::pair<
    std::size_t,
    std::tuple<
      std::optional<
        result_or_monostate<Ts>
      >...
    >
  >
>
when_any(
  scheduler& sched,
  task<Ts>... tasks
);

The actual implementation uses an internal storage alias for those optional result slots.

For a non-void T:

cpp
std::optional<std::decay_t<T>>

For void:

cpp
std::optional<std::monostate>

when_any:

  • requires at least one task
  • starts every supplied task
  • returns the zero-based index of the first completed task
  • populates only the winning result slot
  • returns on success or exception
  • does not automatically cancel losing tasks

Example:

cpp
auto [index, results] = co_await when_any(
  ctx.get_scheduler(),
  first(),
  second()
);

See when_all and when_any.

Errors ​

Async runtime errors use:

cpp
enum class errc : std::uint8_t
{
  ok = 0,

  invalid_argument,
  not_ready,
  timeout,
  canceled,
  closed,
  overflow,

  stopped,
  queue_full,

  rejected,

  not_supported
};

Create an error code with:

cpp
std::error_code make_error_code(
  errc error
) noexcept;

Access the category with:

cpp
const std::error_category&
category() noexcept;

The category name is:

text
async

Async operations commonly report runtime conditions through std::system_error.

Networking also preserves underlying operating-system and Asio error codes.

See Errors.

Networking ​

Public network types live in:

cpp
namespace vix::async::net

The public abstractions are:

text
tcp_endpoint
tcp_stream
tcp_listener

udp_endpoint
udp_datagram
udp_socket

resolved_address
dns_resolver

Network objects are created through factory functions associated with a core io_context.

tcp_endpoint ​

cpp
struct tcp_endpoint
{
  std::string host;
  std::uint16_t port{0};
};

The port is represented in host byte order.

tcp_stream ​

cpp
class tcp_stream;

Connect ​

cpp
virtual core::task<void>
async_connect(
  const tcp_endpoint& endpoint,
  core::cancel_token token = {}
) = 0;

Read ​

cpp
virtual core::task<std::size_t>
async_read(
  std::span<std::byte> buffer,
  core::cancel_token token = {}
) = 0;

Reads up to buffer.size() bytes.

Write ​

cpp
virtual core::task<std::size_t>
async_write(
  std::span<const std::byte> buffer,
  core::cancel_token token = {}
) = 0;

Returns the number of bytes written by that operation.

Close ​

cpp
virtual void close() noexcept = 0;

State ​

cpp
virtual bool is_open() const noexcept = 0;

Native handle ​

cpp
virtual int native_handle();

Implementations that do not expose a native socket handle may throw std::runtime_error.

Factory ​

cpp
std::unique_ptr<tcp_stream>
make_tcp_stream(
  core::io_context& ctx
);

See TCP.

tcp_listener ​

cpp
class tcp_listener;

Listen ​

cpp
virtual core::task<void>
async_listen(
  const tcp_endpoint& endpoint,
  int backlog = 128
) = 0;

Accept ​

cpp
virtual core::task<
  std::unique_ptr<tcp_stream>
>
async_accept(
  core::cancel_token token = {}
) = 0;

Close ​

cpp
virtual void close() noexcept = 0;

State ​

cpp
virtual bool is_open() const noexcept = 0;

Factory ​

cpp
std::unique_ptr<tcp_listener>
make_tcp_listener(
  core::io_context& ctx
);

See TCP.

udp_endpoint ​

cpp
struct udp_endpoint
{
  std::string host;
  std::uint16_t port{0};
};

udp_datagram ​

cpp
struct udp_datagram
{
  udp_endpoint from;
  std::size_t bytes{0};
};

from identifies the sender.

bytes is the number of bytes written into the receive buffer.

udp_socket ​

cpp
class udp_socket;

Bind ​

cpp
virtual core::task<void>
async_bind(
  const udp_endpoint& endpoint
) = 0;

Send ​

cpp
virtual core::task<std::size_t>
async_send_to(
  std::span<const std::byte> buffer,
  const udp_endpoint& destination,
  core::cancel_token token = {}
) = 0;

Receive ​

cpp
virtual core::task<udp_datagram>
async_recv_from(
  std::span<std::byte> buffer,
  core::cancel_token token = {}
) = 0;

Close ​

cpp
virtual void close() noexcept = 0;

State ​

cpp
virtual bool is_open() const noexcept = 0;

Factory ​

cpp
std::unique_ptr<udp_socket>
make_udp_socket(
  core::io_context& ctx
);

See UDP.

resolved_address ​

cpp
struct resolved_address
{
  std::string ip;
  std::uint16_t port{0};
};

dns_resolver ​

cpp
class dns_resolver;

Resolve a hostname and port:

cpp
virtual core::task<
  std::vector<resolved_address>
>
async_resolve(
  std::string host,
  std::uint16_t port,
  core::cancel_token token = {}
) = 0;

Factory:

cpp
std::unique_ptr<dns_resolver>
make_dns_resolver(
  core::io_context& ctx
);

See DNS.

Version ​

Version constants live in:

cpp
namespace vix::async

The current module version is:

text
1.2.1

Available constants:

cpp
inline constexpr int version_major = 1;
inline constexpr int version_minor = 2;
inline constexpr int version_patch = 1;

inline constexpr const char*
version_prerelease = "";

inline constexpr const char*
version_metadata = "";

inline constexpr const char*
version_string = "1.2.1";

inline constexpr int abi_version = 1;

For example:

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

int main()
{
  vix::print(
    "Vix Async",
    vix::async::version_string
  );

  return 0;
}

Public API map ​

The complete application-facing model is:

text
vix::async
│
├── version
│
├── core
│   ├── task<T>
│   ├── scheduler
│   ├── io_context
│   │
│   ├── cancellation
│   │   ├── cancel_source
│   │   ├── cancel_token
│   │   └── cancel_registration
│   │
│   ├── timer
│   ├── thread_pool
│   ├── signal_set
│   │
│   ├── spawn_detached
│   ├── when_all
│   ├── when_any
│   │
│   └── errors
│       └── errc
│
└── net
    ├── TCP
    │   ├── tcp_endpoint
    │   ├── tcp_stream
    │   └── tcp_listener
    │
    ├── UDP
    │   ├── udp_endpoint
    │   ├── udp_datagram
    │   └── udp_socket
    │
    └── DNS
        ├── resolved_address
        └── dns_resolver

For behavior, execution rules, lifetime, and examples, use the dedicated pages rather than treating this reference as the complete programming guide.

Start with:

Released under the MIT License.