Skip to content

Examples

github-actions[bot] edited this page Jan 28, 2026 · 6 revisions

Examples

This page provides complete, runnable examples demonstrating Elio's features.

Hello World

The simplest Elio program:

#include <elio/elio.hpp>
#include <iostream>

using namespace elio;

coro::task<std::string> get_greeting() {
    co_return "Hello from Elio!";
}

coro::task<void> main_task() {
    std::string greeting = co_await get_greeting();
    std::cout << greeting << std::endl;
    co_return;
}

int main() {
    runtime::scheduler sched(2);
    sched.start();
    
    auto t = main_task();
    sched.spawn(t.release());
    
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
    sched.shutdown();
    return 0;
}

Chained Coroutines

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<void> pipeline() {
    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;
}

TCP Echo Server

A 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};
std::atomic<int> g_listener_fd{-1};

// 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;
    
    // Close listener to interrupt pending accept
    int fd = g_listener_fd.exchange(-1);
    if (fd >= 0) ::close(fd);
    
    co_return;
}

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;
        
        co_await stream.write(buffer, result.result);
    }
    
    ELIO_LOG_INFO("[Client {}] Disconnected", id);
    co_return;
}

coro::task<void> server(uint16_t port, runtime::scheduler& sched) {
    auto& ctx = io::default_io_context();
    auto listener = net::tcp_listener::bind(net::ipv4_address(port), ctx);
    
    if (!listener) {
        ELIO_LOG_ERROR("Failed to bind");
        co_return;
    }
    
    g_listener_fd.store(listener->fd());
    ELIO_LOG_INFO("Server listening on port {}", port);
    
    int client_id = 0;
    while (g_running) {
        auto stream = co_await listener->accept();
        if (!stream) continue;
        
        auto handler = handle_client(std::move(*stream), ++client_id);
        sched.spawn(handler.release());
    }
    co_return;
}

int main() {
    // Block signals BEFORE creating scheduler threads
    signal_set sigs{SIGINT, SIGTERM};
    sigs.block_all_threads();
    
    runtime::scheduler sched(4);
    sched.start();
    
    // Spawn signal handler coroutine
    auto sig_handler = signal_handler_task();
    sched.spawn(sig_handler.release());
    
    auto srv = server(8080, sched);
    sched.spawn(srv.release());
    
    while (g_running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    sched.shutdown();
    return 0;
}

UDS Echo Server

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};
std::atomic<int> g_listener_fd{-1};

// 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;
    int fd = g_listener_fd.exchange(-1);
    if (fd >= 0) ::close(fd);
    
    co_return;
}

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;
        
        co_await stream.write(buffer, result.result);
    }
    
    ELIO_LOG_INFO("[Client {}] Disconnected", id);
    co_return;
}

coro::task<void> server(const net::unix_address& addr, runtime::scheduler& sched) {
    auto& ctx = io::default_io_context();
    
    net::uds_options opts;
    opts.unlink_on_bind = true;
    
    auto listener = net::uds_listener::bind(addr, ctx, opts);
    
    if (!listener) {
        ELIO_LOG_ERROR("Failed to bind to {}", addr.to_string());
        co_return;
    }
    
    g_listener_fd.store(listener->fd());
    ELIO_LOG_INFO("Server listening on {}", addr.to_string());
    
    int client_id = 0;
    while (g_running) {
        auto stream = co_await listener->accept();
        if (!stream) continue;
        
        auto handler = handle_client(std::move(*stream), ++client_id);
        sched.spawn(handler.release());
    }
    co_return;
}

int main() {
    // Block signals BEFORE creating scheduler threads
    signal_set sigs{SIGINT, SIGTERM};
    sigs.block_all_threads();
    
    // Use filesystem socket
    net::unix_address addr("/tmp/echo.sock");
    // Or use abstract socket (Linux-specific):
    // auto addr = net::unix_address::abstract("echo_server");
    
    runtime::scheduler sched(4);
    sched.start();
    
    // Spawn signal handler
    auto sig_handler = signal_handler_task();
    sched.spawn(sig_handler.release());
    
    auto srv = server(addr, sched);
    sched.spawn(srv.release());
    
    while (g_running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    sched.shutdown();
    return 0;
}

UDS Client

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(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);
    }
    
    co_return;
}

int main() {
    // Match server's socket path
    net::unix_address addr("/tmp/echo.sock");
    // Or abstract socket:
    // auto addr = net::unix_address::abstract("echo_server");
    
    runtime::scheduler sched(2);
    sched.start();
    
    std::atomic<bool> done{false};
    auto run = [&]() -> coro::task<void> {
        co_await client_main(addr);
        done = true;
    };
    
    auto t = run();
    sched.spawn(t.release());
    
    while (!done) {
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
    
    sched.shutdown();
    return 0;
}

HTTP Client

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<void> http_examples(io::io_context& ctx) {
    client_config config;
    config.user_agent = "elio-example/1.0";
    config.follow_redirects = true;
    
    client c(ctx, 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;
}

HTTP/2 Client

Making HTTP/2 requests with multiplexed streams:

#include <elio/elio.hpp>
#include <elio/http/http2.hpp>

using namespace elio;
using namespace elio::http;

coro::task<void> http2_examples(io::io_context& ctx) {
    h2_client_config config;
    config.user_agent = "elio-example/1.0";
    config.max_concurrent_streams = 100;
    
    h2_client client(ctx, 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()));
    }
    
    // Multiple requests on same connection (HTTP/2 multiplexing)
    ELIO_LOG_INFO("=== HTTP/2 Multiplexing ===");
    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());
        }
    }
    // All requests above reused the same underlying connection
    
    co_return;
}

HTTP Server

A simple REST API server:

#include <elio/elio.hpp>
#include <elio/http/http.hpp>

using namespace elio;
using namespace elio::http;

coro::task<void> router(request& req, response& resp) {
    auto path = req.path();
    auto method = req.method();
    
    if (method == method::GET && path == "/") {
        resp.set_status(status::ok);
        resp.set_header("Content-Type", "text/html");
        resp.set_body("<h1>Welcome to Elio!</h1>");
    }
    else if (method == method::GET && path == "/api/status") {
        resp.set_status(status::ok);
        resp.set_header("Content-Type", "application/json");
        resp.set_body(R"({"status": "ok", "version": "1.0"})");
    }
    else if (method == method::POST && path == "/api/echo") {
        resp.set_status(status::ok);
        resp.set_header("Content-Type", req.content_type());
        resp.set_body(req.body());
    }
    else {
        resp.set_status(status::not_found);
        resp.set_header("Content-Type", "application/json");
        resp.set_body(R"({"error": "Not Found"})");
    }
    
    co_return;
}

int main() {
    runtime::scheduler sched(4);
    sched.start();
    
    server_config config;
    config.port = 8080;
    config.handler = router;
    
    // Start server...
    
    sched.shutdown();
    return 0;
}

Parallel Tasks

Running multiple tasks concurrently:

#include <elio/elio.hpp>
#include <vector>

using namespace elio;

coro::task<int> compute(int id, int value) {
    ELIO_LOG_INFO("Task {} computing...", id);
    // Simulate work
    co_return value * value;
}

coro::task<void> parallel_compute(runtime::scheduler& sched) {
    std::vector<coro::task<int>> tasks;
    
    // Create multiple tasks
    for (int i = 0; i < 10; ++i) {
        tasks.push_back(compute(i, i + 1));
    }
    
    // Spawn all tasks
    for (auto& t : tasks) {
        sched.spawn(t.release());
    }
    
    co_return;
}

Thread Affinity

Binding vthreads to specific worker threads:

#include <elio/elio.hpp>
#include <iostream>

using namespace elio;

// Task that stays pinned 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 - will not be stolen by other workers
    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();
    co_return;
}

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

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) {
        auto t = pinned_worker(i);
        sched->spawn(t.release());
    }
    
    co_await time::sleep_for(std::chrono::milliseconds(100));
    co_return 0;
}

ELIO_ASYNC_MAIN(async_main)

Timer Example

Using timers for delays:

#include <elio/elio.hpp>

using namespace elio;

coro::task<void> delayed_task(io::io_context& ctx) {
    ELIO_LOG_INFO("Starting...");
    
    co_await time::sleep(ctx, std::chrono::seconds(1));
    ELIO_LOG_INFO("1 second passed");
    
    co_await time::sleep(ctx, std::chrono::milliseconds(500));
    ELIO_LOG_INFO("500ms more passed");
    
    co_return;
}

Exception Handling

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<void> error_handling() {
    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;
}

Synchronization

Using mutex and condition variables:

#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) {
        auto lock = co_await g_mutex.lock();
        ++g_counter;
        ELIO_LOG_DEBUG("Task {} incremented to {}", id, g_counter);
    }
    co_return;
}

coro::task<void> run_concurrent(runtime::scheduler& sched) {
    // Spawn multiple incrementers
    for (int i = 0; i < 4; ++i) {
        auto task = increment(i);
        sched.spawn(task.release());
    }
    co_return;
}

Building Examples

All examples are built automatically with CMake:

cd build
make

# Run individual examples
./examples/hello_world
./examples/tcp_echo_server
./examples/uds_echo_server /tmp/echo.sock
./examples/uds_echo_server @my_socket    # Abstract socket
./examples/uds_echo_client /tmp/echo.sock
./examples/http_client https://httpbin.org/get
./examples/http2_client https://nghttp2.org/
./examples/signal_handling              # Signal handling example

See Also

Clone this wiki locally