{"id":35365565,"url":"https://github.com/dankmeme01/arc","last_synced_at":"2026-02-14T03:43:02.153Z","repository":{"id":325000156,"uuid":"1097142276","full_name":"dankmeme01/arc","owner":"dankmeme01","description":"Modern C++ async runtime, inspired by Tokio","archived":false,"fork":false,"pushed_at":"2026-02-05T01:49:06.000Z","size":445,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-05T01:54:07.866Z","etag":null,"topics":["async","asynchronous","cpp","runtime"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dankmeme01.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-11-15T16:05:01.000Z","updated_at":"2026-02-05T01:48:03.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/dankmeme01/arc","commit_stats":null,"previous_names":["dankmeme01/arc"],"tags_count":18,"template":false,"template_full_name":null,"purl":"pkg:github/dankmeme01/arc","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankmeme01%2Farc","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankmeme01%2Farc/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankmeme01%2Farc/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankmeme01%2Farc/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dankmeme01","download_url":"https://codeload.github.com/dankmeme01/arc/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dankmeme01%2Farc/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29206818,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-07T17:44:10.191Z","status":"ssl_error","status_checked_at":"2026-02-07T17:44:07.936Z","response_time":63,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6: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":["async","asynchronous","cpp","runtime"],"created_at":"2026-01-02T01:51:31.735Z","updated_at":"2026-02-14T03:43:02.140Z","avatar_url":"https://github.com/dankmeme01.png","language":"C++","readme":"# Arc\n\nArc is a modern C++ async runtime heavily inspired by Tokio and Rust async in general. If you've programmed async rust before - this library will be very familiar to you.\n\nThis project is WIP, some things may have bugs and not be production ready, but it is actively maintained and some things are covered by tests. Features (scroll below for examples):\n* Runtime that can run using either one or multiple threads\n* Tasks as an independent unit of execution\n* Blocking tasks on a thread pool\n* Synchronization (Mutexes, semaphores, notify, MPSC channels)\n* Networking (UDP sockets, TCP sockets and listeners)\n* Time utilities (sleep, interval, timeout)\n* Multi-future pollers like `arc::select` and `arc::joinAll`\n* Signal catching (i.e. listening for Ctrl+C easily)\n* Top-level exception handler that prints the backtrace of futures, to aid in debugging\n\nTODO:\n* File IO using blocking thread pool\n* Better poller. Current implementation uses `poll`/`WSAPoll`, which isn't scalable. This is not an issue for small jobs, but makes the library unsuitable for servers that handle hundreds of connections. Currently this is a non-goal as this library was mostly made for a network client rather than a server\n\n## Getting Started\n\nArc supports Clang and MSVC and requires C++23. If you are using CMake with CPM, the easiest way to use Arc is as follows:\n```cmake\nCPMAddPackage(\"gh:dankmeme01/arc@v1.1.0\")\ntarget_link_libraries(mylib PRIVATE arc)\n```\n\nBy default, this includes all features (networking, time, signals, etc.) except for debugging ones. For fine-grained feature control, disable `ARC_FEATURE_FULL`:\n```cmake\n# The line below will disable less essential (but not all) parts of Arc\nset(ARC_FEATURE_FULL OFF CACHE BOOL \"\" FORCE)\n\n# Manual feature selection\nset(ARC_FEATURE_TIME OFF CACHE BOOL \"\" FORCE)\nset(ARC_FEATURE_NET ON CACHE BOOL \"\" FORCE)\nset(ARC_FEATURE_SIGNAL OFF CACHE BOOL \"\" FORCE)\nset(ARC_FEATURE_DEBUG ON CACHE BOOL \"\" FORCE)\nset(ARC_FEATURE_TRACE OFF CACHE BOOL \"\" FORCE)\n```\n\nTo run any async code, you must have a runtime. Arc runtimes do not need to be unique or persistent, there is no global singleton runtime and you are responsible for creating one yourself. If you are a library developer and want to use Arc, you can spin up a runtime and run code like this:\n```cpp\n#include \u003carc/prelude.hpp\u003e\n\narc::Future\u003cint\u003e myFuture() {\n    fmt::println(\"Hello from async!\");\n    co_return 42;\n}\n\nauto rt = arc::Runtime::create(4); // use 4 threads, omit to use the CPU thread count\n\n// this will wait for `myFuture` to finish and return its result\nint value = rt-\u003eblockOn(myFuture());\n\n// this will spawn the future independently and not block\n// note that the future will be aborted if the runtime is destroyed\nauto handle = rt-\u003espawn(myFuture());\n```\n\nIf you are an application developer, you can use a helper macro to automatically make a runtime for you:\n```cpp\n#include \u003carc/runtime/Main.hpp\u003e\n\narc::Future\u003c\u003e asyncMain(int argc, const char** argv) {\n    fmt::println(\"Hello from async!\");\n    co_return;\n}\n\nARC_DEFINE_MAIN(asyncMain);\n// alternatively, if you want to specify thread count\nARC_DEFINE_MAIN_NT(asyncMain, 4);\n```\n\nWhen using the helper macro, the arguments of the main function must be either `()`, `(int, char**)` or `(int, const char**)`. The return value can be:\n* `int` (aka `arc::Future\u003cint\u003e`) or any `T` that is convertible to `int` - value will be used as the exit code\n* `void` (aka `arc::Future\u003c\u003e` or `arc::Future\u003cvoid\u003e`) - exit code will be 0\n* `geode::Result\u003cvoid, E\u003e` where `E` can be formatted with `fmt` - returning an `Err` will print the error and exit with code 1\n\n## Examples\n\nAll examples here may not be fully complete and are simplified for demonstration purposes. The `examples` subfolder contains examples of actual compilable programs. Some additional documentation is available [here](./docs/index.md)\n\nCreating a runtime and blocking on a single async function, creating tasks\n```cpp\n#include \u003carc/prelude.hpp\u003e\nusing namespace asp::time;\n\narc::Future\u003cint\u003e noop() {\n    // `yield` temporarily yields control to the scheduler, like a very short sleep\n    co_await arc::yield();\n    co_return 1;\n}\n\narc::Future\u003c\u003e asyncMain() {\n    // `spawn` can be used to spawn a task and let it run in the background,\n    // the coroutine will be running in parallel and not block the current task\n    auto handle = arc::spawn(noop());\n\n    // Async sleep, does not block the thread and yields to the runtime instead.\n    co_await arc::sleepFor(Duration::fromSecs(1));\n\n    // One second later, let's retrieve the value returned by the spawned task\n    int value = co_await handle;\n    fmt::println(\"{}\", value);\n}\n\nARC_DEFINE_MAIN(asyncMain);\n```\n\nRunning a task every X seconds (interval)\n```cpp\n// create an interval that ticks every 250 milliseconds\nauto interval = arc::interval(Duration::fromMillis(250));\n\nwhile (true) {\n    co_await interval.tick();\n    fmt::println(\"tick!\");\n}\n```\n\nSending data between tasks or between sync code\n```cpp\narc::Task\u003c\u003e consumer(arc::mpsc::Receiver\u003cint\u003e rx) {\n    while (true) {\n        auto val = co_await rx.recv();\n\n        if (!val.isOk()) {\n            break; // channel is closed now, all senders have been destroyed\n        }\n\n        fmt::println(\"received value: {}\", val.unwrap());\n    }\n}\n\narc::Task\u003c\u003e asyncMain() {\n    // Create a new MPSC channel with unlimited capacity\n    auto [tx, rx] = arc::mpsc::channel\u003cint\u003e();\n\n    // Spawn a consumer task\n    arc::spawn(consumer(std::move(rx)));\n\n    // Sender can be copied, unlike the Receiver\n    auto tx2 = tx;\n\n    // Send can only fail if the channel has been closed (meaning the receiver no longer exists)\n    (void) co_await tx.send(1);\n\n    // `trySend` can be used in sync or async code, will fail if the channel is closed or full\n    auto res = tx2.trySend(2);\n}\n```\n\nSynchronization utilities such as Mutex, Notify, Semaphore\n```cpp\narc::Future\u003c\u003e asyncMain() {\n    arc::Mutex\u003cint\u003e mtx{0};\n    arc::Notify notify;\n\n    // spawn a task that will wait for a notification\n    // if you're confused about `this auto self`, scroll to the bottom of README\n    auto handle = arc::spawn([\u0026](this auto self) -\u003e arc::Future\u003cint\u003e {\n        co_await notify.notified();\n\n        // try to lock the mutex, this will take some time because main function waits before unlocking\n        auto lock = co_await mtx.lock();\n        co_return *lock;\n    }());\n\n    {\n        // lock the mutex and change the value\n        fmt::println(\"Locking mutex in main\");\n        auto lock = co_await mtx.lock();\n        *lock = 42;\n\n        // notify the other task and wait a bit before unlocking\n        fmt::println(\"Notifying task\");\n        notify.notifyOne();\n        co_await arc::sleep(Duration::fromSecs(1));\n\n        fmt::println(\"Unlocking mutex in main\");\n    }\n\n    int value = co_await handle;\n    fmt::println(\"{}\", value);\n}\n```\n\nCreating TCP and UDP sockets\n```cpp\n// TcpStream is very similar to rust's TcpStream\nauto res = co_await arc::TcpStream::connect(\"127.0.0.1:8000\");\nauto socket = std::move(res).unwrap();\n\n// In the real world, check that the functions actually succeed instead of casting to void/unwrapping\nchar[] data = \"hello world\";\n(void) co_await socket.send(data, sizeof(data));\nchar buf[512];\nsize_t n = (co_await socket.receive(buf, 512)).unwrap();\n\n// UdpSocket\nauto res = co_await arc::UdpSocket::bindAny();\nauto socket = std::move(res).unwrap();\n\nauto dest = qsox::SocketAddress::parse(\"127.0.0.1:1234\").unwrap();\nchar[] data = \"hello world\";\n(void) co_await socket.sendTo(data, sizeof(data), dest);\nchar buf[512];\nsize_t n = (co_await socket.receive(buf, 512)).unwrap();\n```\n\nCreating a TCP listener\n```cpp\nauto res = co_await arc::TcpListener::bind(\n    qsox::SocketAddress::parse(\"0.0.0.0:4242\").unwrap()\n);\nauto listener = std::move(res).unwrap();\n\nwhile (true) {\n    auto res = co_await listener.accept();\n    auto [stream, addr] = std::move(res).unwrap();\n\n    fmt::println(\"Accepted connection from {}\", addr.toString());\n\n    arc::spawn([](arc::TcpStream s, qsox::SocketAddress a) mutable -\u003e arc::Future\u003c\u003e {\n        // do things with the socket ...\n    }(std::move(stream), addr));\n}\n```\n\nPutting a time limit on a future, and cancelling it if it doesn't complete in time.\n```cpp\nauto [tx, rx] = arc::mpsc::channel\u003cint\u003e();\n\n// Wait until we either get a value, or don't get any values in 5 seconds.\nauto res = co_await arc::timeout(\n    Duration::fromSecs(5),\n    rx.recv()\n);\n\nif (res.isErr()) {\n    fmt::println(\"Timed out!\");\n    co_return;\n}\n\nauto result = std::move(res).unwrap();\nif (result.isOk()) {\n    fmt::println(\"Value: {}\", result.unwrap());\n} else {\n    fmt::println(\"Channel closed!\");\n}\n```\n\nRun multiple futures concurrently (as part of one task), wait for one of them to complete and cancel the losers. This is very similar to the `tokio::select!` macro in Rust and can be incredibly useful.\n\n```cpp\narc::Mutex\u003cint\u003e mtx;\n\n// arc::select takes an unlimited list of selectees.\n// Whenever the first one of them completes, its callback is invoked (if any),\n// and the rest are immediately cancelled.\nco_await arc::select(\n    // A future that simply finishes in 5 seconds\n    // (basically ensuring the select won't last longer than that)\n    arc::selectee(\n        arc::sleep(Duration::fromSecs(5)),\n        [] { fmt::println(\"Time elapsed!\"); }\n    ),\n\n    // A future that never completes, just for showcase purposes\n    arc::selectee(arc::never()),\n\n    // A future that will complete once we are able to\n    // acquire the lock on the mutex\n    arc::selectee(\n        mtx.lock(),\n        [](auto guard) { fmt::println(\"Value: {}\", *guard); },\n        // Passing `false` as the 3rd argument to `selectee` will\n        // disable this branch from being polled.\n        false\n    ),\n\n    // A future that waits for an interrupt (Ctrl+C) signal to be sent\n    arc::selectee(\n        arc::ctrl_c(),\n        // Callbacks can be synchronous, but they also can be futures\n        [] -\u003e arc::Future\u003c\u003e {\n            fmt::println(\"Ctrl+C received, exiting!\");\n            co_return;\n        }\n    )\n);\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankmeme01%2Farc","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdankmeme01%2Farc","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdankmeme01%2Farc/lists"}