Repository navigation
Examples
This page provides complete, runnable examples demonstrating Elio's features.
The simplest Elio program using ELIO_ASYNC_MAIN:
#include <elio/elio.hpp>
#include <iostream>
using namespace elio;
coro::task<std::string> get_greeting() {
co_return "Hello from Elio!";
}
coro::task<int> async_main(int argc, char* argv[]) {
std::string greeting = co_await get_greeting();
std::cout << greeting << std::endl;
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Demonstrating coroutine composition:
#include <elio/elio.hpp>
using namespace elio;
coro::task<int> step1() {
ELIO_LOG_INFO("Step 1");
co_return 10;
}
coro::task<int> step2(int input) {
ELIO_LOG_INFO("Step 2: input={}", input);
co_return input * 2;
}
coro::task<int> step3(int input) {
ELIO_LOG_INFO("Step 3: input={}", input);
co_return input + 5;
}
coro::task<int> async_main(int argc, char* argv[]) {
int a = co_await step1(); // 10
int b = co_await step2(a); // 20
int c = co_await step3(b); // 25
ELIO_LOG_INFO("Final result: {}", c);
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)examples/join_destroyed_async.cpp
is a runnable example of the distinction between task result readiness and final
capture destruction. Await the result first, then
co_await handle.wait_destroyed_async() before releasing resources the callable
borrows. This second await does not consume the result, cancel the task, or occupy
a blocking-pool thread. Pending observers require a live Elio scheduler; normal
drain, not forced frame destruction, establishes the capture lifetime boundary.
cmake --build build --target join_destroyed_async --parallel 2
./build/examples/join_destroyed_async
# Result: 42, captures released: yesA concurrent TCP server that echoes data back to clients, using signalfd for graceful shutdown:
#include <elio/elio.hpp>
#include <atomic>
using namespace elio;
using namespace elio::signal;
std::atomic<bool> g_running{true};
coro::cancel_source g_accept_cancel;
// Signal handler coroutine - waits for SIGINT/SIGTERM
coro::task<void> signal_handler_task() {
signal_set sigs{SIGINT, SIGTERM};
signal_fd sigfd(sigs);
auto info = co_await sigfd.wait();
if (info) {
ELIO_LOG_INFO("Received signal: {}", info->full_name());
}
g_running = false;
g_accept_cancel.cancel();
}
coro::task<void> handle_client(net::tcp_stream stream, int id) {
ELIO_LOG_INFO("[Client {}] Connected", id);
char buffer[1024];
while (g_running) {
auto result = co_await stream.read(buffer, sizeof(buffer));
if (result.result <= 0) break;
auto written = co_await stream.write_exactly(buffer, result.result);
if (written.result <= 0) break;
}
ELIO_LOG_INFO("[Client {}] Disconnected", id);
}
coro::task<int> async_main(int argc, char* argv[]) {
// Spawn signal handler
elio::go(signal_handler_task);
auto listener = net::tcp_listener::bind(net::ipv4_address(8080));
if (!listener) {
ELIO_LOG_ERROR("Failed to bind");
co_return 1;
}
ELIO_LOG_INFO("Server listening on port 8080");
int client_id = 0;
while (g_running) {
auto stream = co_await listener->accept(g_accept_cancel.get_token());
if (!stream) {
if (!g_running || g_accept_cancel.is_cancelled()) break;
continue;
}
elio::go([stream = std::move(*stream), id = ++client_id]() mutable {
return handle_client(std::move(stream), id);
});
}
co_return 0;
}
int main(int argc, char* argv[]) {
// Block shutdown signals before scheduler worker threads are created
signal_set sigs{SIGINT, SIGTERM};
sigs.block_all_threads();
return elio::run(async_main, argc, argv);
}A concurrent Unix Domain Socket server that echoes data back to clients:
#include <elio/elio.hpp>
#include <atomic>
using namespace elio;
using namespace elio::signal;
std::atomic<bool> g_running{true};
coro::cancel_source g_accept_cancel;
// Signal handler coroutine
coro::task<void> signal_handler_task() {
signal_set sigs{SIGINT, SIGTERM};
signal_fd sigfd(sigs);
auto info = co_await sigfd.wait();
if (info) {
ELIO_LOG_INFO("Received signal: {}", info->full_name());
}
g_running = false;
g_accept_cancel.cancel();
}
coro::task<void> handle_client(net::uds_stream stream, int id) {
ELIO_LOG_INFO("[Client {}] Connected", id);
char buffer[1024];
while (g_running) {
auto result = co_await stream.read(buffer, sizeof(buffer));
if (result.result <= 0) break;
auto written = co_await stream.write_exactly(buffer, result.result);
if (written.result <= 0) break;
}
ELIO_LOG_INFO("[Client {}] Disconnected", id);
}
coro::task<int> async_main(int argc, char* argv[]) {
// Spawn signal handler
elio::go(signal_handler_task);
// Use filesystem socket
net::unix_address addr("/tmp/echo.sock");
// Or use abstract socket (Linux-specific):
// auto addr = net::unix_address::abstract("echo_server");
net::uds_options opts;
opts.unlink_on_bind = true;
auto listener = net::uds_listener::bind(addr, opts);
if (!listener) {
ELIO_LOG_ERROR("Failed to bind to {}", addr.to_string());
co_return 1;
}
ELIO_LOG_INFO("Server listening on {}", addr.to_string());
int client_id = 0;
while (g_running) {
auto stream = co_await listener->accept(g_accept_cancel.get_token());
if (!stream) {
if (!g_running || g_accept_cancel.is_cancelled()) break;
continue;
}
elio::go([stream = std::move(*stream), id = ++client_id]() mutable {
return handle_client(std::move(stream), id);
});
}
co_return 0;
}
int main(int argc, char* argv[]) {
// Block shutdown signals before scheduler worker threads are created
signal_set sigs{SIGINT, SIGTERM};
sigs.block_all_threads();
return elio::run(async_main, argc, argv);
}A Unix Domain Socket client that connects to a UDS server:
#include <elio/elio.hpp>
using namespace elio;
coro::task<void> client_main(const net::unix_address& addr) {
ELIO_LOG_INFO("Connecting to {}...", addr.to_string());
auto stream = co_await net::uds_connect(addr);
if (!stream) {
ELIO_LOG_ERROR("Connect failed: {}", strerror(errno));
co_return;
}
ELIO_LOG_INFO("Connected!");
// Send message
const char* msg = "Hello via Unix Domain Socket!";
co_await stream->write_exactly(msg, strlen(msg));
// Receive echo
char buffer[1024];
auto result = co_await stream->read(buffer, sizeof(buffer) - 1);
if (result.result > 0) {
buffer[result.result] = '\0';
ELIO_LOG_INFO("Received: {}", buffer);
}
}
coro::task<int> async_main(int argc, char* argv[]) {
// Match server's socket path
net::unix_address addr("/tmp/echo.sock");
// Or abstract socket:
// auto addr = net::unix_address::abstract("echo_server");
co_await client_main(addr);
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)For a self-contained HTTP/1 range read into caller storage without mandatory
response-body aggregation, see
examples/http_streaming_client.cpp.
It validates final status/Content-Range before pulling body bytes, preserves
borrowed buffer lifetime, observes explicit framing completion, and settles its
owned loopback peer. Its 64 KiB streamed body exceeds the configured 1 KiB
buffered-response limit. HTTP scratch-to-destination copying remains; see
HTTP Streaming.
cmake --build build --target http_streaming_client --parallel 2
./build/examples/http_streaming_client
# Streamed range: 65536 bytes; response body not aggregatedhttp_client_result.cpp is an offline loopback example of owned error values
and normal non-2xx responses. Build/run http_client_result with HTTP enabled:
it retains an invalid-target error while a local peer returns HTTP 503, without
reading errno or requiring external DNS/network access. Use
std::get_if<http::client_error>() to distinguish failure from a response;
retry/idempotency policy remains application-owned.
Making HTTP requests with various methods:
#include <elio/elio.hpp>
#include <elio/http/http.hpp>
#include <elio/tls/tls.hpp>
using namespace elio;
using namespace elio::http;
coro::task<int> async_main(int argc, char* argv[]) {
client_config config;
config.user_agent = "elio-example/1.0";
config.follow_redirects = true;
client c(config);
// GET request
ELIO_LOG_INFO("=== GET ===");
auto get_resp = co_await c.get("https://httpbin.org/get");
if (get_resp) {
ELIO_LOG_INFO("Status: {}", get_resp->status_code());
}
// POST JSON
ELIO_LOG_INFO("=== POST JSON ===");
auto post_resp = co_await c.post(
"https://httpbin.org/post",
R"({"name": "Elio"})",
mime::application_json
);
if (post_resp) {
ELIO_LOG_INFO("Status: {}", post_resp->status_code());
}
// POST Form
ELIO_LOG_INFO("=== POST Form ===");
auto form_resp = co_await c.post(
"https://httpbin.org/post",
"key=value&foo=bar",
mime::application_form_urlencoded
);
if (form_resp) {
ELIO_LOG_INFO("Status: {}", form_resp->status_code());
}
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Making HTTP/2 requests with sequential connection reuse:
#include <elio/elio.hpp>
#include <elio/http/http2.hpp>
using namespace elio;
using namespace elio::http;
coro::task<int> async_main(int argc, char* argv[]) {
h2_client_config config;
config.user_agent = "elio-example/1.0";
config.max_concurrent_streams = 100;
h2_client client(config);
// GET request (HTTP/2 requires HTTPS)
ELIO_LOG_INFO("=== HTTP/2 GET ===");
auto get_resp = co_await client.get("https://nghttp2.org/");
if (get_resp) {
ELIO_LOG_INFO("Status: {}", static_cast<int>(get_resp->get_status()));
ELIO_LOG_INFO("Body size: {} bytes", get_resp->body().size());
}
// POST JSON
ELIO_LOG_INFO("=== HTTP/2 POST JSON ===");
auto post_resp = co_await client.post(
"https://httpbin.org/post",
R"({"name": "Elio", "protocol": "h2"})",
mime::application_json
);
if (post_resp) {
ELIO_LOG_INFO("Status: {}", static_cast<int>(post_resp->get_status()));
}
// Sequential requests can reuse pooled HTTP/2 connections.
ELIO_LOG_INFO("=== HTTP/2 Connection Reuse ===");
for (int i = 0; i < 5; ++i) {
auto resp = co_await client.get("https://nghttp2.org/");
if (resp) {
ELIO_LOG_INFO("Request {}: {} bytes", i + 1, resp->body().size());
}
}
// Requests above are awaited sequentially; use separate clients for
// parallel high-level requests until shared-session multiplexing is
// implemented.
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)A REST API server using the router and elio::serve for graceful shutdown:
#include <elio/elio.hpp>
#include <elio/http/http.hpp>
using namespace elio;
using namespace elio::http;
response hello_handler(context& ctx) {
return response(status::ok, "<h1>Welcome to Elio!</h1>", mime::text_html);
}
coro::task<response> status_handler(context& ctx) {
co_return response(status::ok,
R"({"status": "ok", "version": "1.0"})",
mime::application_json);
}
coro::task<response> echo_handler(context& ctx) {
auto& req = ctx.req();
response resp(status::ok);
resp.set_header("Content-Type", req.content_type());
resp.set_body(req.body());
co_return resp;
}
coro::task<int> async_main(int argc, char* argv[]) {
router r;
r.get("/", hello_handler);
r.get("/api/status", status_handler);
r.post("/api/echo", echo_handler);
server srv(r);
co_await elio::serve(srv, [&]() { return srv.listen(net::ipv4_address(8080)); });
co_return 0;
}
int main(int argc, char* argv[]) {
elio::signal::signal_set shutdown_signals(elio::default_shutdown_signals);
shutdown_signals.block_all_threads();
return elio::run(async_main, argc, argv);
}Running multiple tasks concurrently using spawn() and go():
#include <elio/elio.hpp>
#include <vector>
using namespace elio;
coro::task<int> compute(int id, int value) {
ELIO_LOG_INFO("Task {} computing...", id);
co_return value * value;
}
coro::task<int> async_main(int argc, char* argv[]) {
// Spawn tasks and collect join handles to await results
std::vector<coro::join_handle<int>> handles;
for (int i = 0; i < 10; ++i) {
handles.push_back(elio::spawn(compute, i, i + 1));
}
// Await all results
int total = 0;
for (auto& h : handles) {
total += co_await h;
}
ELIO_LOG_INFO("Sum of squares: {}", total);
// Fire-and-forget tasks (no result collection)
for (int i = 0; i < 5; ++i) {
elio::go(compute, i, i);
}
co_await time::sleep_for(std::chrono::milliseconds(100));
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Binding vthreads to specific worker threads:
#include <elio/elio.hpp>
#include <iostream>
using namespace elio;
// Task with affinity to a specific worker
coro::task<void> pinned_worker(size_t target_worker) {
// Bind to target worker and migrate there
co_await set_affinity(target_worker);
std::cout << "Running on worker " << current_worker_id() << std::endl;
// Do work - steal attempts bounce the task back to this worker
for (int i = 0; i < 5; ++i) {
co_await time::yield();
// Still on the same worker
}
// Clear affinity to allow migration
co_await clear_affinity();
}
// Task that pins to its current worker
coro::task<void> stay_here() {
co_await bind_to_current_worker();
// Will remain on this worker for rest of execution
std::cout << "Pinned to worker " << current_worker_id() << std::endl;
}
coro::task<int> async_main(int argc, char* argv[]) {
auto* sched = runtime::scheduler::current();
// Spawn tasks with different affinities
for (size_t i = 0; i < sched->num_threads(); ++i) {
elio::go(pinned_worker, i);
}
co_await time::sleep_for(std::chrono::milliseconds(100));
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Using timers for delays:
#include <elio/elio.hpp>
using namespace elio;
coro::task<int> async_main(int argc, char* argv[]) {
ELIO_LOG_INFO("Starting...");
co_await time::sleep_for(std::chrono::seconds(1));
ELIO_LOG_INFO("1 second passed");
co_await time::sleep_for(std::chrono::milliseconds(500));
ELIO_LOG_INFO("500ms more passed");
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Handling errors in coroutines:
#include <elio/elio.hpp>
#include <stdexcept>
using namespace elio;
coro::task<int> may_fail(bool should_fail) {
if (should_fail) {
throw std::runtime_error("Something went wrong!");
}
co_return 42;
}
coro::task<int> async_main(int argc, char* argv[]) {
try {
int result = co_await may_fail(false);
ELIO_LOG_INFO("Success: {}", result);
int fail = co_await may_fail(true); // Throws
ELIO_LOG_INFO("Never reached: {}", fail);
} catch (const std::exception& e) {
ELIO_LOG_ERROR("Caught: {}", e.what());
}
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)Using async mutex:
#include <elio/elio.hpp>
using namespace elio;
sync::mutex g_mutex;
int g_counter = 0;
coro::task<void> increment(int id) {
for (int i = 0; i < 100; ++i) {
co_await g_mutex.lock();
++g_counter;
ELIO_LOG_DEBUG("Task {} incremented to {}", id, g_counter);
g_mutex.unlock();
}
}
coro::task<int> async_main(int argc, char* argv[]) {
// Spawn multiple incrementers and collect handles
std::vector<coro::join_handle<void>> handles;
for (int i = 0; i < 4; ++i) {
handles.push_back(elio::spawn(increment, i));
}
// Wait for all to finish
for (auto& h : handles) {
co_await h;
}
ELIO_LOG_INFO("Final counter: {}", g_counter);
co_return 0;
}
ELIO_ASYNC_MAIN(async_main)A minimal RPC server and client using Elio's binary RPC framework:
#include <elio/elio.hpp>
#include <elio/rpc/rpc.hpp>
using namespace elio;
// Define messages
struct GreetRequest {
std::string name;
ELIO_RPC_FIELDS(GreetRequest, name);
};
struct GreetResponse {
std::string message;
ELIO_RPC_FIELDS(GreetResponse, message);
};
// Define method (id=1)
using Greet = ELIO_RPC_METHOD(1, GreetRequest, GreetResponse);
// Server
coro::task<void> run_server() {
auto listener = net::tcp_listener::bind(net::ipv4_address("0.0.0.0", 9000));
if (!listener) co_return;
rpc::tcp_rpc_server server;
server.register_method<Greet>([](const GreetRequest& req) -> coro::task<GreetResponse> {
co_return GreetResponse{.message = "Hello, " + req.name + "!"};
});
co_await server.serve(*listener);
}
// Client
coro::task<void> run_client() {
auto client = co_await rpc::tcp_rpc_client::connect("127.0.0.1", 9000);
if (!client) co_return;
auto result = co_await (*client)->call<Greet>(GreetRequest{.name = "World"});
if (result) {
ELIO_LOG_INFO("Response: {}", result->message);
}
}Using CRC32 and SHA-256 hash functions:
#include <elio/hash/sha256.hpp>
#include <elio/hash/crc32.hpp>
#include <elio/hash/hash.hpp>
using namespace elio::hash;
void hash_example() {
// One-shot SHA-256
std::string hex = sha256_hex("Hello, World!");
// Incremental hashing
sha256_context ctx;
ctx.update("Hello, ");
ctx.update("World!");
auto digest = ctx.finalize();
std::string hex2 = to_hex(digest);
// CRC32 checksum
uint32_t crc = crc32("data", 4);
// CRC32 over scatter-gather buffers
struct iovec iov[2] = {
{(void*)"Hello", 5},
{(void*)" World", 6}
};
uint32_t sg_crc = crc32_iovec(iov, 2);
}#include <elio/elio.hpp>
#include <iostream>
using namespace elio;
coro::task<void> file_operations() {
// Read a file
auto content = co_await io::read_file("/etc/hostname");
if (content) {
std::cout << "Hostname: " << *content << std::endl;
}
// Write a file
bool ok = co_await io::write_file("/tmp/hello.txt", "Hello, Elio!\n");
if (ok) {
std::cout << "File written successfully" << std::endl;
}
// Append to a file
ok = co_await io::append_file("/tmp/hello.txt", "Appended line\n");
// Check file metadata
if (io::file_exists("/tmp/hello.txt")) {
auto size = io::file_size("/tmp/hello.txt");
std::cout << "File size: " << (size ? *size : 0) << " bytes" << std::endl;
}
// Read a directory
auto entries = io::read_dir("/tmp");
if (entries) {
for (const auto& entry : *entries) {
if (entry.is_file) {
std::cout << "[FILE] " << entry.name << std::endl;
} else if (entry.is_dir) {
std::cout << "[DIR] " << entry.name << std::endl;
}
}
}
}examples/positional_file_io.cpp
writes an exact region of a temporary regular file, then reads a larger region
and observes both the bytes transferred and EOF. It keeps the descriptor and
buffers alive across the awaited calls and removes the temporary filename.
std::array<char, 64> header{};
auto result = co_await io::pread_exactly(
fd, std::as_writable_bytes(std::span(header)), offset);
if (result.end != io::transfer_end::complete) {
// Decide whether partial bytes are useful or EOF is a format-level error.
report_incomplete_header(result.transferred, result.end, result.error);
}pread_some/pwrite_some stop after one positive completion; exact helpers
retain byte progress after later errors. Optional tokens stop only before the
next operation, without abandoning admitted I/O or adding a deadline. Keep the
regular-file FD open and unrecycled until return; writes require no O_APPEND.
Exact transfer does not imply durability, atomicity, or a stable-file snapshot,
and the existing epoll inline execution policy still applies.
cmake --build build --target positional_file_io --parallel 2
./build/examples/positional_file_ioexamples/file_persistence.cpp
allocates a temporary file where supported, truncates it, and synchronizes it
before releasing the descriptor. The example treats filesystem allocation
capability errors explicitly and keeps the FD alive across awaited calls.
auto resized = co_await io::truncate_file(fd, new_length, token);
if (!resized) co_return resized.error_value();
auto synced = co_await io::sync_file(fd, io::file_sync_mode::data_only, token);
if (!synced) co_return synced.error_value();
// Application-side metadata publication and directory sync follow separately.These operations require a running scheduler worker. Native capabilities are probed;
fallback work uses bounded fixed-pool admission. Queued cancellation can skip
dispatch, but running syscalls still complete before normal return. No deadline,
rollback, or transaction guarantee is supplied. Keep the FD open and unrecycled,
and handle EAGAIN admission rejection according to application policy.
cmake --build build --target file_persistence --parallel 2
./build/examples/file_persistenceRead multiple file regions. Explicit offsets can be batched through io_uring; negative offsets read from the descriptor's current position in segment order:
coro::task<void> batch_read_example(int fd) {
char header[64] = {0};
char footer[64] = {0};
char middle[128] = {0};
std::array<io::batch_read_segment, 3> segments;
segments[0] = {0, header, 64}; // Read first 64 bytes
segments[1] = {1024, middle, 128}; // Read 128 bytes at offset 1024
segments[2] = {-1, footer, 64}; // Read from current position
auto results = co_await io::batch_read(fd, segments);
for (size_t i = 0; i < results.size(); ++i) {
if (results[i] > 0) {
std::cout << "Segment " << i << ": read " << results[i] << " bytes" << std::endl;
} else {
std::cerr << "Segment " << i << ": error " << -results[i] << std::endl;
}
}
}Write multiple positioned file regions simultaneously:
coro::task<void> batch_write_example(int fd) {
const char* part1 = "HEADER";
const char* part2 = "FOOTER";
std::array<io::batch_write_segment, 2> segments;
segments[0] = {0, part1, 6}; // Write at start
segments[1] = {1024, part2, 6}; // Write at offset 1024
auto results = co_await io::batch_write(fd, segments);
// Both writes happen concurrently in the kernel
}Configure with ELIO_BUILD_EXAMPLES=ON to build examples. Some example groups
are only created when their feature targets are enabled:
- HTTP, WebSocket, SSE, and HTTP client examples require
elio_http. - HTTP/2 examples require
elio_http2. - RDMA examples require the matching
elio_rdma*targets. - TCP benchmark comparison programs require
ELIO_BUILD_TCP_BENCHMARKS=ON; install libuv development files (libuv1-devon Debian/Ubuntu) if you needbench_tcp_libuv.
cmake -S . -B build -DELIO_BUILD_EXAMPLES=ON
cmake --build build --target hello_world tcp_echo_server uds_echo_server
# Run individual examples
./build/examples/hello_world
./build/examples/tcp_echo_server
./build/examples/uds_echo_server /tmp/echo.sock
./build/examples/uds_echo_server @my_socket # Abstract socket
./build/examples/uds_echo_client /tmp/echo.sock
# Feature-gated examples are available when their targets were configured
./build/examples/http_client https://httpbin.org/get
./build/examples/http2_client https://nghttp2.org/
./build/examples/signal_handling # Signal handling exampleAutomatic worker thread scaling based on load:
#include <elio/elio.hpp>
#include <elio/runtime/autoscaler.hpp>
#include <atomic>
#include <chrono>
#include <random>
using namespace elio;
// Task that simulates work
coro::task<void> workload_task(std::atomic<int>& counter) {
static thread_local std::mt19937 rng(
std::hash<std::thread::id>{}(std::this_thread::get_id())
);
std::uniform_int_distribution<int> dist(1, 50);
std::this_thread::sleep_for(std::chrono::milliseconds(dist(rng)));
counter.fetch_add(1, std::memory_order_relaxed);
co_return;
}
int main() {
// Configure autoscaler
elio::runtime::autoscaler_config config;
config.tick_interval = std::chrono::milliseconds(200);
config.overload_threshold = 20; // Scale up when queue > 20
config.idle_threshold = 5; // Scale down when queue < 5
config.idle_delay = std::chrono::seconds(5);
config.min_workers = 2;
config.max_workers = 8;
// Create scheduler and autoscaler
elio::runtime::scheduler sched(2);
sched.start();
elio::runtime::autoscaler<elio::runtime::scheduler,
elio::runtime::on_overload<elio::runtime::scale_up<elio::runtime::null>>,
elio::runtime::on_idle<elio::runtime::scale_down<elio::runtime::null>>,
elio::runtime::on_block<elio::runtime::log>
> autoscaler(config);
autoscaler.start(&sched);
// Submit workload...
std::atomic<int> completed{0};
for (int i = 0; i < 1000; ++i) {
sched.go(workload_task, std::ref(completed));
}
// Monitor autoscaler behavior
while (completed.load() < 1000) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "Workers: " << sched.num_threads()
<< ", Pending: " << sched.pending_tasks() << std::endl;
}
autoscaler.stop();
sched.shutdown();
}The autoscaler supports:
-
Triggers:
on_overload,on_idle,on_block -
Actions:
scale_up,scale_down,log,null -
Combinators:
on_success,on_failure
The complete autoscaler_example retains join handles for every submitted load
phase and waits for all task frames to be destroyed before releasing captured
phase state or beginning its low-load observation. Use
./build/examples/autoscaler_example --smoke for a short termination check.
on_block is a best-effort sampled diagnostic. A non-idle worker is considered
blocked when its completed-resume counter does not advance for longer than
block_threshold; detection can trail the threshold by approximately one
tick_interval.
A single-process SEND/RECV ping-pong using a mock backend — no RDMA hardware required. Demonstrates connection, dispatcher, cq_pump, and wc_result. See examples/rdma_pingpong_mock.cpp.
// Abridged — see full source for mock_backend and mock_qp wiring.
#include <elio/elio.hpp>
#include <elio/rdma/rdma.hpp>
using elio::rdma::connection;
using elio::rdma::buffer_view;
// A side: send "ping N", await echoed reply.
elio::coro::task<void> ping_side(connection<mock_backend>& conn) {
std::vector<char> tx(128), rx(128);
for (int i = 0; i < 16; ++i) {
auto recv_aw = conn.recv(buffer_view{rx.data(), rx.size(), 0}).start();
std::string msg = "ping " + std::to_string(i);
std::memcpy(tx.data(), msg.data(), msg.size());
co_await conn.send(buffer_view{tx.data(), msg.size(), 0});
auto wc = co_await std::move(recv_aw);
// wc.byte_len tells us how much was echoed
}
}Build: cmake -B build -DELIO_ENABLE_RDMA=ON && cmake --build build --target rdma_pingpong_mock
Client sends a request via SEND, server writes the response with RDMA WRITE and signals completion with SEND_WITH_IMM. Uses endpoint, acceptor, connect from elio::rdma_ibverbs. Requires a working uverbs ABI (Soft-RoCE / real HCA). See examples/rdma_req_resp_ibverbs.cpp.
// Abridged client side — see full source for the server.
#include <elio/rdma/rdma.hpp>
#include <elio/rdma_cm/rdma_cm.hpp>
#include <elio/rdma_ibverbs/rdma_ibverbs.hpp>
elio::coro::task<void> client(elio::runtime::scheduler& sched) {
elio::rdma_cm::event_channel cm_ch;
auto ep = co_await elio::rdma_ibverbs::connect(
cm_ch, dst_addr, sizeof(*dst_addr),
{.max_send_wr = 4, .max_recv_wr = 4});
ep.start_cq_pump(sched);
{
auto req_mr = ep.register_buffer(
req_buf, sizeof(req_buf), IBV_ACCESS_LOCAL_WRITE);
auto resp_mr = ep.register_buffer(
resp_buf, sizeof(resp_buf),
IBV_ACCESS_LOCAL_WRITE | IBV_ACCESS_REMOTE_WRITE);
std::uint32_t notify_buf{};
auto notify_mr = ep.register_buffer(
¬ify_buf, sizeof(notify_buf), IBV_ACCESS_LOCAL_WRITE);
// Post recv for OOB notify BEFORE sending the request.
auto notify_aw = ep.conn().recv(notify_mr.view()).start();
co_await ep.conn().send(req_mr.view(0, sizeof(request_header)));
auto wc = co_await std::move(notify_aw);
// wc.imm_data carries the response length
} // Every endpoint-backed MR and awaiter is gone before shutdown.
co_await ep.shutdown();
}Build: cmake -B build -DELIO_ENABLE_RDMA=ON -DELIO_ENABLE_RDMA_CM=ON -DELIO_ENABLE_RDMA_IBVERBS=ON && cmake --build build --target rdma_req_resp_ibverbs
- RDMA-Guide - Full RDMA abstraction layer guide
- Signal-Handling - Detailed guide on signal handling with signalfd
- RPC-Framework - RPC system documentation
- Hash-Functions - Hash function documentation
- Core-Concepts - Understanding Elio's architecture
- Networking - TCP and HTTP usage