Skip to content

C++ Stream Connector Async Runtime Guide

This document explains how a client using the core connector must handle callbacks and dispatch(). Internal socket structure and Boost.Asio implementation details aren't public usage, so they aren't described here.

Dispatch Mode

In manual mode, a user callback runs on the thread that called dispatch(). Engine wrappers like Unreal, Godot, and Axmol use this mode to run a delegate or signal on the engine main thread.

In immediate mode, the runtime worker that produced the completion can run the user callback. Used in code with no engine main-thread constraint, like a CLI, tool, or perf client.

Callback Completion

request().submit(callback), wait_for().submit(callback), connect(callback), close(callback) register the operation and return immediately. The synchronous request().submit(), wait_for().submit(), connect(), close() are kept for existing users.

On TCP, TLS, WebSocket, and WSS transport, connect(callback) doesn't block the calling thread to wait for connection completion or the handshake. TLS and WSS can be used in a build with the OpenSSL feature turned on.

send().submit() submits a one-way send request and doesn't return a completion result to the caller. Send acceptance and backpressure handling are the connector's internal responsibility.

request().submit(callback) returns after registering the request-frame write. When one of a reply, timeout, close, or transport error occurs, it's delivered to the callback as a result_t<T>. While waiting for a reply, neither the calling thread nor a worker thread is tied up in a blocking wait.

wait_for().submit(callback) also waits via callback completion until one of a matching packet, timeout, or close occurs. It doesn't occupy the calling thread while waiting.

It must be possible to call the connector API again from inside a callback. So the implementation doesn't call the user callback while holding the connector's internal lock.

If the connector closes, a callback operation that hasn't finished yet must not remain as a success. If the user called close() or close(callback) to shut it down, it completes with a closed error. If the user canceled the operation via a coroutine task or an explicit cancellation token, a canceled error is used. A transport drop is still distinguished as disconnected, as before.

Engine Rule

An engine wrapper doesn't expose core types in its public API. A core callback is put into the adapter queue, then converted and run as an engine delegate on the engine's Tick, Update, or an explicit Dispatch().