{"id":45979740,"url":"https://github.com/ynachi/kio","last_synced_at":"2026-02-28T17:32:13.947Z","repository":{"id":323145252,"uuid":"1069568882","full_name":"ynachi/kio","owner":"ynachi","description":"Iouring based asynchronous IO lib","archived":false,"fork":false,"pushed_at":"2026-02-10T21:50:15.000Z","size":3496,"stargazers_count":9,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-11T00:18:15.772Z","etag":null,"topics":["asynchronous","bitcask","bitcask-storage-engine","coroutines","cpp20","database","files","io-uring","keyvaluestore","linux","storage-engine"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ynachi.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-04T07:22:38.000Z","updated_at":"2026-02-03T19:04:38.000Z","dependencies_parsed_at":"2026-02-02T22:01:42.304Z","dependency_job_id":null,"html_url":"https://github.com/ynachi/kio","commit_stats":null,"previous_names":["ynachi/kio"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/ynachi/kio","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ynachi%2Fkio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ynachi%2Fkio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ynachi%2Fkio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ynachi%2Fkio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ynachi","download_url":"https://codeload.github.com/ynachi/kio/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ynachi%2Fkio/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29944767,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-28T13:49:17.081Z","status":"ssl_error","status_checked_at":"2026-02-28T13:48:50.396Z","response_time":90,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["asynchronous","bitcask","bitcask-storage-engine","coroutines","cpp20","database","files","io-uring","keyvaluestore","linux","storage-engine"],"created_at":"2026-02-28T17:32:13.269Z","updated_at":"2026-02-28T17:32:13.923Z","avatar_url":"https://github.com/ynachi.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# AIO - Modern C++ Async I/O Library\n\nA high-performance, coroutine-based asynchronous I/O library for Linux built on `io_uring`.\nTests and demo are built with TSAN enabled and are currently free from any issue.\n\n## Features\n\n- **C++20 Coroutines** - Natural async/await syntax with zero-overhead abstractions\n- **io_uring Backend** - Leverages Linux's most efficient async I/O interface\n- **Single-Threaded Per-Core** - Thread-per-core architecture for predictable performance\n- **Zero-Copy I/O** - Efficient `splice`/`sendfile` support via pipe pools\n- **Lazy Timeout Management** - O(1) per-operation overhead, optimized for the common case\n- **Structured Concurrency** - `TaskGroup` for safe concurrent task management\n- **Blocking Pool** - Offload blocking operations without stalling the event loop\n- **Built-in Observability** - Optional statistics and async logging\n\n## Requirements\n\n- Linux kernel 5.11+ (for full io_uring feature support)\n- GCC 14+ or Clang 15+ (C++20 coroutine support and c++23 features)\n- liburing 2.5+\n- OpenSSL 3.2+ (for TLS support)\n\n## Quick Start\n\n### Hello World, TCP Echo Server\n\n```cpp\n#include \u003caio/aio.hpp\u003e\n\nusing namespace aio;\nusing namespace std::chrono_literals;\n\nTask\u003c\u003e HandleClient(IoContext\u0026 ctx, net::Socket client)\n{\n    std::array\u003cstd::byte, 4096\u003e buf{};\n    \n    while (true)\n    {\n        // Read with 30 second timeout\n        auto result = co_await AsyncRecv(ctx, client, buf).WithTimeout(30s);\n        \n        if (!result || *result == 0)\n            break;  // Error or client disconnected\n        \n        // Echo back\n        co_await AsyncSend(ctx, client, std::span{buf.data(), *result});\n    }\n}\n\nTask\u003c\u003e Server(IoContext\u0026 ctx, uint16_t port)\n{\n    auto listener = net::TcpListener::BindV4(port);\n    if (!listener)\n        throw std::runtime_error(\"Failed to bind\");\n    \n    TaskGroup tasks;\n    \n    while (true)\n    {\n        auto accepted = co_await AsyncAccept(ctx, *listener);\n        if (!accepted)\n            continue;\n        \n        tasks.Spawn(HandleClient(ctx, net::Socket(accepted-\u003efd)));\n    }\n}\n\nint main()\n{\n    IoContext ctx;\n    ctx.RunUntilDone(Server(ctx, 8080));\n    return 0;\n}\n```\n\n### Multi-Core Server\n\n```cpp\n#include \u003caio/aio.hpp\u003e\n\nint main()\n{\n    const size_t num_cores = std::thread::hardware_concurrency();\n    std::vector\u003caio::Worker\u003e workers;\n    \n    std::stop_source stop;\n    \n    for (size_t i = 0; i \u003c num_cores; ++i)\n    {\n        workers.emplace_back(i);\n        workers.back().Start(\n            [st = stop.get_token()](aio::IoContext\u0026 ctx)\n            {\n                ctx.RunUntilDone(Server(ctx, 8080, st));\n            },\n            i  // Pin to CPU core\n        );\n    }\n    \n    // Wait for signal\n    aio::IoContext main_ctx;\n    aio::SignalSet signals{SIGINT, SIGTERM};\n    main_ctx.RunUntilDone([\u0026]() -\u003e aio::Task\u003c\u003e {\n        co_await aio::AsyncWaitSignal(main_ctx, signals);\n        stop.request_stop();\n    }());\n    \n    for (auto\u0026 w : workers)\n        w.Join();\n    \n    return 0;\n}\n```\n\n## Core Concepts\n\n### IoContext\n\nThe event loop. Each thread should have exactly one `IoContext`.\n\n```cpp\nIoContext ctx(1024);  // 1024 SQ entries\n\n// Run until stopped\nctx.Run();\n\n// Run until a specific task completes\nctx.RunUntilDone(MyTask(ctx));\n\n// Run with periodic tick callback\nctx.Run([\u0026] { ProcessMetrics(); });\n\n// Stop the loop (thread-safe)\nctx.Stop();\nctx.Notify();  // Wake if blocked in io_uring_wait\n```\n\n### Task\u003cT\u003e\n\nThe coroutine return type. They are lazy, they don't start until awaited.\n\n```cpp\nTask\u003cint\u003e ComputeAsync(IoContext\u0026 ctx)\n{\n    co_await AsyncSleep(ctx, 100ms);\n    co_return 42;\n}\n\nTask\u003c\u003e Caller(IoContext\u0026 ctx)\n{\n    int result = co_await ComputeAsync(ctx);\n    // result == 42\n}\n```\n\n### Result\u003cT\u003e\n\nError handling via `std::expected\u003cT, std::error_code\u003e`.\n\n```cpp\nTask\u003c\u003e Example(IoContext\u0026 ctx, int fd)\n{\n    auto result = co_await AsyncRead(ctx, fd, buffer);\n    \n    if (!result)\n    {\n        // Handle error\n        std::cerr \u003c\u003c \"Read failed: \" \u003c\u003c result.error().message() \u003c\u003c \"\\n\";\n        co_return;\n    }\n    \n    size_t bytes_read = *result;\n}\n```\n\n## Async Operations\n\n### File I/O\n\n```cpp\n// Open\nauto fd = co_await AsyncOpen(ctx, \"/path/to/file\", O_RDONLY);\n\n// Read/Write\nauto n = co_await AsyncRead(ctx, fd, buffer, offset);\nauto n = co_await AsyncWrite(ctx, fd, data, offset);\n\n// Vectored I/O\nauto n = co_await AsyncReadv(ctx, fd, iovecs, offset);\nauto n = co_await AsyncWritev(ctx, fd, iovecs, offset);\n\n// Close\nco_await AsyncClose(ctx, fd);\n\n// File operations\nco_await AsyncFsync(ctx, fd);\nco_await AsyncFallocate(ctx, fd, mode, offset, len);\nco_await AsyncFtruncate(ctx, fd, length);\nco_await AsyncUnlink(ctx, \"/path/to/file\");\nco_await AsyncRename(ctx, \"/old/path\", \"/new/path\");\nco_await AsyncMkdir(ctx, \"/path/to/dir\", 0755);\n```\n\n### Network I/O\n\n```cpp\n// TCP Server\nauto listener = net::TcpListener::BindV4(8080);\nauto accepted = co_await AsyncAccept(ctx, *listener);\nnet::Socket client(accepted-\u003efd);\n\n// TCP Client\nnet::Socket sock(socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0));\nauto addr = net::SocketAddress::V4(8080, \"127.0.0.1\");\nco_await AsyncConnect(ctx, sock, addr);\n\n// Send/Recv\nauto n = co_await AsyncSend(ctx, sock, data);\nauto n = co_await AsyncRecv(ctx, sock, buffer);\n\n// With flags\nauto n = co_await AsyncSend(ctx, sock, data, MSG_NOSIGNAL);\nauto n = co_await AsyncRecv(ctx, sock, buffer, MSG_WAITALL);\n\n// Sendmsg/Recvmsg (for advanced use cases)\nco_await AsyncSendmsg(ctx, sock, \u0026msg, flags);\nco_await AsyncRecvmsg(ctx, sock, \u0026msg, flags);\n```\n\n### Zero-Copy File Transfer\n\n```cpp\n// Send file to socket efficiently using splice\nco_await AsyncSendfile(ctx, socket, file_fd, offset, count);\n```\n\n### Timeouts\n\n```cpp\n// Per-operation timeout\nauto result = co_await AsyncRecv(ctx, sock, buf).WithTimeout(5s);\n\nif (!result \u0026\u0026 result.error() == std::errc::timed_out)\n{\n    // Handle timeout\n}\n\n// Sleep\nco_await AsyncSleep(ctx, 100ms);\n```\n\n### Polling\n\n```cpp\n// Wait for readability\nauto events = co_await AsyncPoll(ctx, fd, POLLIN);\n\n// Wait for writability\nauto events = co_await AsyncPoll(ctx, fd, POLLOUT);\n```\n\n## Structured Concurrency\n\n### TaskGroup\n\nManages a collection of concurrent tasks with automatic lifetime management.\n\n```cpp\nTask\u003c\u003e ProcessConnections(IoContext\u0026 ctx)\n{\n    TaskGroup tasks;\n    \n    // Spawn concurrent tasks\n    tasks.Spawn(HandleClient(ctx, client1));\n    tasks.Spawn(HandleClient(ctx, client2));\n    tasks.Spawn(HandleClient(ctx, client3));\n    \n    // Or spawn multiple at once\n    tasks.SpawnAll(\n        Task1(ctx),\n        Task2(ctx),\n        Task3(ctx)\n    );\n    \n    // Wait for all to complete\n    co_await tasks.JoinAll(ctx);\n    \n    // Or with timeout\n    bool completed = co_await tasks.JoinAllTimeout(ctx, 30s);\n}\n```\n\n### Notifier\n\nCross-thread notification primitive.\n\n```cpp\nNotifier notifier;\n\n// Waiting side (coroutine)\nTask\u003c\u003e Waiter(IoContext\u0026 ctx)\n{\n    co_await notifier.Wait(ctx);\n    // Signaled!\n}\n\n// Signaling side (any thread)\nnotifier.Signal();\n```\n\n## Blocking Operations\n\nUse `BlockingPool` to offload blocking operations without stalling the event loop.\n\n```cpp\nBlockingPool pool(4);  // 4 worker threads\n\nTask\u003c\u003e Example(IoContext\u0026 ctx)\n{\n    // Offload CPU-intensive work\n    auto result = co_await Offload(ctx, pool, [] {\n        return ExpensiveComputation();\n    });\n    \n    // Offload blocking syscalls\n    auto dns_result = co_await Offload(ctx, pool, [] {\n        return getaddrinfo(...);\n    });\n}\n```\n\n### Async DNS Resolution\n\n```cpp\n// Synchronous (blocking - use only at startup)\nauto addr = net::Resolve(\"example.com\", 443);\n\n// Asynchronous (non-blocking)\nauto addr = co_await net::ResolveAsync(ctx, pool, \"example.com\", 443);\n```\n\n## Signal Handling\n\n```cpp\nSignalSet signals{SIGINT, SIGTERM};\n\nTask\u003c\u003e WaitForShutdown(IoContext\u0026 ctx)\n{\n    auto sig = co_await AsyncWaitSignal(ctx, signals);\n    std::cout \u003c\u003c \"Received signal: \" \u003c\u003c *sig \u003c\u003c \"\\n\";\n}\n```\n\n## Worker Threads\n\nConvenience wrapper for running IoContext on dedicated threads.\n\n```cpp\nWorker worker;\n\n// Low-level: full control\nworker.Start([](IoContext\u0026 ctx) {\n    ctx.Run();\n}, 0);  // Pin to CPU 0\n\n// Run a task to completion\nworker.RunTask([](IoContext\u0026 ctx) -\u003e Task\u003c\u003e {\n    co_await MyServerTask(ctx);\n}, 0);\n\n// Run with tick callback\nworker.RunLoop([\u0026] { UpdateStats(); }, 0);\n\n// Stop and wait\nworker.RequestStop();\nworker.Join();\n```\n\n## Logging\n\nBuilt-in async logger with minimal overhead.\n\n```cpp\n#include \u003caio/logger.hpp\u003e\n\n// Configure\naio::alog::g_level = aio::alog::Level::Info;\naio::alog::g_colors = true;\n\n// Log\nALOG_DEBUG(\"Debug message: {}\", value);\nALOG_INFO(\"Connection from {}:{}\", ip, port);\nALOG_WARN(\"High latency: {}ms\", latency);\nALOG_ERROR(\"Failed to open file: {}\", path);\nALOG_FATAL(\"Unrecoverable error\");\n\n// Check dropped messages (if queue overflows)\nuint64_t dropped = aio::alog::dropped_count();\n```\n\nCompile-time filtering:\n\n```cpp\n// In CMakeLists.txt or compile flags\nadd_definitions(-DLOG_BUILD_LEVEL=1)  // 0=Debug, 1=Info, 2=Warn, 3=Error\n```\n\n## Statistics\n\nOptional performance counters (compile with `-DAIO_STATS=1`).\n\n```cpp\n#define AIO_STATS 1\n#include \u003caio/aio.hpp\u003e\n\nIoContext ctx;\n// ... run workload ...\n\nauto stats = ctx.Stats().GetSnapshot();\nstd::cout \u003c\u003c \"Submitted: \" \u003c\u003c stats.ops_submitted \u003c\u003c \"\\n\";\nstd::cout \u003c\u003c \"Completed: \" \u003c\u003c stats.ops_completed \u003c\u003c \"\\n\";\nstd::cout \u003c\u003c \"Errors: \" \u003c\u003c stats.ops_errors \u003c\u003c \"\\n\";\nstd::cout \u003c\u003c \"Timeouts: \" \u003c\u003c stats.timeouts \u003c\u003c \"\\n\";\nstd::cout \u003c\u003c \"Max inflight: \" \u003c\u003c stats.ops_max_inflight \u003c\u003c \"\\n\";\nstd::cout \u003c\u003c \"Loop iterations: \" \u003c\u003c stats.loop_iterations \u003c\u003c \"\\n\";\n```\n\n## IoBuffer\n\nHigh-performance ring buffer for protocol parsing.\n\n```cpp\nIoBuffer buf(4096);\n\n// Read into buffer\nauto writable = buf.WritableBytesSpan();\nauto n = co_await AsyncRecv(ctx, sock, writable);\nbuf.Commit(*n);\n\n// Process data\nauto readable = buf.ReadableSpan();\nsize_t consumed = ParseProtocol(readable);\nbuf.Consume(consumed);\n\n// Build response\nbuf.Append(\"HTTP/1.1 200 OK\\r\\n\");\nbuf.Append(\"Content-Length: 5\\r\\n\\r\\n\");\nbuf.Append(\"Hello\");\nbuf.Commit();\n\n// Send\nco_await AsyncSend(ctx, sock, buf.ReadableBytesSpan());\n```\n\n## Socket Options\n\n```cpp\nnet::Socket sock(fd);\n\nsock.SetNonBlocking();\nsock.SetReuseAddr();\nsock.SetReusePort();\nsock.SetNodelay();\nsock.SetSendBuffer(65536);\nsock.SetRecvBuffer(65536);\n```\n\n## Performance Tips\n\n### 1. Use Thread-Per-Core\n\nEach `IoContext` is single-threaded. Scale by running one per CPU core.\n\n```cpp\nfor (size_t i = 0; i \u003c num_cores; ++i)\n{\n    workers.emplace_back(i);\n    workers.back().Start(WorkerFunc, i);  // Pin to core i\n}\n```\n\n### 2. Batch Operations\n\nUse vectored I/O when possible:\n\n```cpp\n// Instead of multiple sends\nco_await AsyncSend(ctx, sock, header);\nco_await AsyncSend(ctx, sock, body);\n\n// Use writev\nstd::array\u003ciovec, 2\u003e iov = {{{header.data(), header.size()}, {body.data(), body.size()}}};\nco_await AsyncWritev(ctx, sock, iov);\n```\n\n### 3. Avoid Unnecessary Timeouts\n\nTimeouts have overhead. For connection-level idle detection, consider a periodic scan instead of per-operation timeouts.\nPer operation timeout is expensive as it requires 2X io uring submit system calls.\n\n### 4. Use Zero-Copy for Large Transfers\n\n```cpp\n// For serving files\nco_await AsyncSendfile(ctx, socket, file_fd, 0, file_size);\n```\n\n### 5. Pre-size Buffers\n\n```cpp\nIoBuffer buf;\nbuf.Reserve(expected_max_size);  // Avoid reallocations\n```\n\n## Building\n\nLook at [build](./docs/build.md) docs.\n\n### CMake\n\n```cmake\ncmake_minimum_required(VERSION 3.28)\nproject(myapp)\n\nset(CMAKE_CXX_STANDARD 23)\n\nadd_executable(myapp main.cpp)\ntarget_link_libraries(myapp kio)\n\n# Optional: Enable stats\ntarget_compile_definitions(myapp PRIVATE AIO_STATS=1)\n```\n\n\n## Architecture\n\n```\n┌─────────────────────────────────────────────────────────────────────┐\n│                         Application Layer                           │\n│   ┌─────────────┐  ┌─────────────┐  ┌─────────────┐                 │\n│   │  Coroutine  │  │  Coroutine  │  │  Coroutine  │  ...            │\n│   │   Task\u003cT\u003e   │  │   Task\u003cT\u003e   │  │   Task\u003cT\u003e   │                 │\n│   └──────┬──────┘  └──────┬──────┘  └──────┬──────┘                 │\n│          │                │                │                        │\n│          └────────────────┼────────────────┘                        │\n│                           │ co_await                                │\n├───────────────────────────┼─────────────────────────────────────────┤\n│                     kio I/O Layer                                   │\n│   ┌───────────────────────┴───────────────────────┐                 │\n│   │              Async Operations                 │                 │\n│   │  AsyncRead, AsyncWrite, AsyncRecv, AsyncSend  │                 │\n│   │  AsyncAccept, AsyncConnect, AsyncClose, ...   │                 │\n│   └───────────────────────┬───────────────────────┘                 │\n│                           │                                         │\n│   ┌───────────────────────┴───────────────────────┐                 │\n│   │               IoContext                       │                 │\n│   │  - Owns io_uring ring                         │                 │\n│   │  - Tracks pending operations                  │                 │\n│   │  - Manages completion queue                   │                 │\n│   │  - Resumes coroutines on completion           │                 │\n│   └───────────────────────┬───────────────────────┘                 │\n├───────────────────────────┼─────────────────────────────────────────┤\n│                     Linux Kernel                                    │\n│   ┌───────────────────────┴───────────────────────┐                 │\n│   │               io_uring                        │                 │\n│   │  ┌─────────────┐       ┌─────────────┐        │                 │\n│   │  │ Submission  │       │ Completion  │        │                 │\n│   │  │   Queue     │  ───► │   Queue     │        │                 │\n│   │  │   (SQ)      │       │   (CQ)      │        │                 │\n│   │  └─────────────┘       └─────────────┘        │                 │\n│   └───────────────────────────────────────────────┘                 │\n└─────────────────────────────────────────────────────────────────────┘\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fynachi%2Fkio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fynachi%2Fkio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fynachi%2Fkio/lists"}