Self-hosted service
romange/helio avatar
romange/helio

Helio: a C++17 backend framework built on io_uring and fibers

A modern framework for backend development based on io_uring Linux interface

555 stars71 forksC++Apache-2.0

At a glance

What is it?
Helio is a set of C++ primitives for backend development on Linux, with an io_uring-based proactor, a fiber library with fiber-aware synchronisation primitives and an HTTP server built on Boost.Beast. Its own fiber implementation replaced Boost.Fiber in May 2023 so the polling mechanism could be evolved.
Who is it for?
Adopt Helio if you are writing a Linux backend in C++ and want io_uring with a fiber model that keeps the polling mechanism under your control, since the Boost.Fiber fork was taken in-house for exactly that reason. Do not adopt it if you need portability beyond Linux or a batteries-included service framework.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Helio actually is, and what it is not

Helio is described as a modern framework for backend development based on the io_uring Linux interface, and the README title is more precise: a backend development framework in C++ using io_uring and an epoll event-loop.

The README's own list of what it provides is short enough to quote in structure. There is a dependency on abseil-cpp, a dependency on Boost 1.71, the use of ninja-build on top of cmake, and built artifacts that are docker-friendly. Then there is an HTTP server implementation, and a fibers library with fiber-friendly synchronisation primitives.

That list is the scope. This is a set of primitives, not a framework in the sense of a web framework with routing, templates and a plugin system. The base/ directory holds the core utilities, io/ holds the I/O layer, util/ holds higher-level pieces such as the HTTP handling, strings/ is a string library, and examples/ contains worked programs you can read as documentation.

The framing also comes from the project's earlier life. The README opens by describing Async as a set of C++ primitives that allows efficient and rapid development in c++17 on Linux systems, with the focus mostly on backend development and data processing. The repository is now named helio while the README still carries the async name in places, and the CI badge points at the romange/async repository. That is a rename in progress rather than a fresh project, and the codebase carries the history: an io/ directory for the event-driven layer and a base/ directory for the primitives that predate it.

The primary language is C++, the licence is Apache-2.0, the default branch is master, and the homepage is romange.github.io/helio. The topic tags list io-uring, async, fibers and cpp17, which is an accurate summary of what the library is for.

The fibers library and why it was forked out of Boost.Fiber

The most interesting piece of engineering history in this repository is the fibers library, and the README is unusually candid about it.

Helio utilized Boost.Fiber until May 2023. After that period, the fibers functionality was integrated directly into Helio code. The transition involved adapting and reworking portions of the original Boost.Fiber code to align with Helio's coding conventions and architecture.

The stated motivation is the one that matters for anyone extending the library. The decision to replicate and modify the original code was driven by the necessity for greater flexibility. Specifically, it was easier to evolve Helio's polling mechanism when the maintainer had full control over the fibers internal interfaces. If you are going to change how the event loop and the fibers interact, owning the fiber internals is the difference between a pull request upstream and a local patch.

The README then spells out how the two differ, in a section titled Differences between Boost.Fiber and helio fb2 fibers. Helio's fb2 library adopts a similar non-intrusive, shared nothing design to Boost.Fiber. However, Helio's fb2 library is more opinionated and also provides full networking support for asynchronous multi-threaded execution.

Two concrete differences follow from that. Helio fibers allow injecting a custom dispatch policy, util::fb2::DispatchPolicy, directly into a dispatching fiber, while Boost.Fiber requires using a separate fiber to handle the I/O polling. And the Boost.Fiber inter-thread notification mechanism, the remote ready queue, required locking, while Helio fibers use a lockless queue.

That second point is the kind of detail that decides whether a library suits your workload. A lockless ready queue removes a contention point from every cross-thread wakeup, which matters when many fibers on many threads are all waking each other, and it is exactly the sort of thing you cannot retrofit into a third-party implementation.

The credit is explicit, with acknowledgement to @olk and all the contributors of Boost.Fiber for their foundational work, and a statement that the design and implementation of Helio fibers draw significant inspiration from Boost.Fiber, with certain segments selectively adopted and modified.

Building it, and running the echo server

The build is a small shell script workflow rather than a documented package manager flow, and the README's three commands are the whole procedure.

bash
   > sudo ./install-dependencies.sh
   > ./blaze.sh -release
   > cd build-opt && ninja -j4 echo_server

The install script needs sudo, which tells you it is installing system packages for the build, and blaze.sh with the -release flag is the wrapper that configures the cmake build. The third command builds one specific target, the echo_server example, and the -j4 sets parallelism. The README notes that the third_party folder is checked out under the build directories, so the external dependencies are fetched by the build rather than vendored in the source tree.

The result is two binaries you run in two tabs, and the client takes a host, a message count and a connection count.

bash
      server> ./echo_server --logtostderr
      client> ./echo_server --connect=localhost --n 100000 --c=4

So the example is a load-and-echo pair rather than a toy: the client sends 100000 messages over 4 concurrent connections, and the server echoes them back with logging to stderr. That is a reasonable first check that the proactor and the fibers are behaving, and the examples directory holds the other shapes of the same idea: a raw echo server, a pingserver, a proactor_stress program, a redis_dict example, and demos for GCS, S3 and an HTTPS client.

The build system itself follows the conventions the README spells out. Third-party packages have a TRDP:: prefix in CMakeLists.txt, and absl libraries have the prefix absl::... . There is a .clang-format and a .clangd in the tree, plus a pre-commit config with both .pre-commit-config.yaml and .pre-commit-hooks.yaml, so formatting is enforced before code reaches the repository.

The CI badge points at a codecov dashboard for the project, and the tests use a googletest plus gmock unit-test environment.

HTTP, monitoring and self-profiling

The HTTP layer is built on Boost.Beast and integrated with the io_uring-based ProactorPool, and the README points at http_main.cc in util/http as the example to read.

Two things come with it that are more than a request handler. The first is backend monitoring through a Varz status page, and the README describes the behaviour in terms of what the monitor returns: with monitoring enabled, the C++ backend returns a JSON object that is formatted inside the status page in the browser. To see it, you go to localhost:8080 while the echo_server is running.

That Varz approach is worth a moment because it is the older, simpler model of service introspection. The handler registers counters and gauges in the backend code, and a built-in HTTP endpoint renders them. There is no separate agent and no scrape configuration, which is why it still reads well.

The second is self-profiling. Every http-powered backend has integrated CPU profiling capabilities using gperf-tools and pprof, and profiling can be triggered in production using magic-url commands. The README claims enabled profiling usually has a very minimal impact on CPU performance of the running backend, which is the design goal: a profile you cannot take without taking the service down is a profile nobody takes.

Logging is based on Google's glog library. Here the README goes further than a credit, because the maintainer states that Google's version has some bugs which he fixed in his own fork while waiting for review, and says he uses his own fork. The reason he gives for liking glog in the first place is the ability to control the logging levels of a backend at run-time without restarting it. A backend that must be restarted to turn on debug logging cannot be debugged under load, and that is the capability that keeps glog in use despite the fork.

The HTTP handling also includes what the README calls an extensible debugging interface, which is the natural place for a request inspector or a debug overlay when you are developing an endpoint.

Linux system information and cloud platform detection

A smaller but genuinely useful part of the library is the system information layer, and the README documents it in unusual detail, which suggests it has been patched a fair amount.

The header io/proc_reader.h provides ReadCpuInfo() for /proc/cpuinfo, ReadDmiInfo() for whitespace-trimmed sysfs DMI attributes, and the existing process and memory readers. The README is careful about a claim that is easy to overstate: CPU hypervisor status is explicitly unknown, absent, or present, and an absent flag does not prove that the machine is bare metal. That is the correct epistemic position for a heuristic, and the fact that it is written down is a sign the heuristic has been argued about.

ReadCpuInfo() returns io::Result<CpuInfo>, and ReadDmiInfo() retains each attribute's io::Result, so callers can inspect missing or unreadable attributes independently. That is the detail that matters in practice: a single unreadable DMI attribute does not fail the whole read, it shows up as one failed attribute in an otherwise useful result. Both readers accept an alternate path for mounted system trees, which is what lets them work against a container's mounted root rather than the host's /proc.

The cloud detection layer lives separately, in util/cloud/platform_info.h, which provides util::cloud::PlatformInfo::Create() for local cloud, hypervisor and deployment classification, keeping these heuristics out of the I/O layer. The result retains each source's io::Result alongside the classification enums, and detection uses DMI, CPU flags, environment variables and runtime marker files, without contacting metadata services.

Two design choices there are worth naming. Not contacting metadata services is a deliberate decision against the usual cloud detection approach: no network round trip, no dependence on a metadata endpoint being reachable, and no possibility of hanging in a network-restricted environment. And the file-root prefix allows inspecting a mounted system tree while environment markers still describe the current process, which is the behaviour you want when you are inspecting an offline image.

The README also notes that consumers map enums to application-specific labels, which keeps the library from dictating how you name a platform in your logs.

Where Helio is the wrong choice

There are three limits, and the first is the platform itself.

The first is that this is Linux-only in practice. The whole design rests on io_uring, and where that is not available the library falls back to an epoll event loop. The README's title names both, which is fair, but io_uring availability is the reason to care about Helio in the first place, and the epoll path is a compatibility route rather than an equal one. If you need to run the same service on macOS or Windows, this is the wrong dependency.

The second is the maintenance model. The repository has no GitHub releases at all, and the README's own roadmap is a first person statement about gradually adding explanations for the most important blocks in the library. That is a single-maintainer project, which is visible in the details: the glog fork is described as waiting for review, the fiber library is a maintained fork of someone else's code, and the conventions section documents prefix rules rather than a published API stability policy.

That is not a criticism so much as a fact to plan around. The code is Apache-2.0, so you can read it, vendor it and modify it, and the fork history means you will be reading code that someone is actively changing. If you adopt Helio, the realistic expectation is that you are adopting it and then owning the parts of it you depend on, which is a different proposition from installing a package with a version stream.

The third is the scope boundary. There is an HTTP server, a proactor, fibers and synchronisation primitives, but there is no connection pool, no configuration framework, no metrics pipeline beyond the Varz page, and no service mesh story. If you are building an internal service and want a batteries-included framework, the alternative is to write the same thing with one of the established C++ web frameworks, where routing, serialization and connection handling are solved problems.

The honest alternative on the concurrency side is Boost.Asio with its strand-based executor and coroutine support, which is portable, widely deployed and has a much larger user base. The difference in approach is where the control lives. Asio gives you a portable abstraction that you can adapt to io_uring, while Helio gives you direct ownership of the polling mechanism and the fibers that drive it. That ownership is what the Boost.Fiber fork was about: when you own the fiber internals you can evolve the polling mechanism, and when you are using Asio you work within the abstraction its maintainers chose. If you never need to change how I/O polling and scheduling interact, Asio is the lower-risk dependency. If you do, Helio is the one where that change is possible without forking.

Editorial conclusion

Adopt Helio if you are writing a Linux backend in C++ and want io_uring with a fiber model that keeps the polling mechanism under your control, since the Boost.Fiber fork was taken in-house for exactly that reason. Do not adopt it if you need portability beyond Linux or a batteries-included service framework. Before you start, run install-dependencies.sh and note that the repository has no GitHub releases despite a push on 2026-10-19.

Frequently asked questions

What is Helio and what does it provide?

Helio is a set of C++17 primitives for backend development on Linux, using io_uring with an epoll event loop. It depends on abseil-cpp and Boost 1.71, builds with cmake and ninja, produces docker-friendly artifacts, and includes an HTTP server implementation plus a fibers library with fiber-friendly synchronisation primitives.

How does Helio's fiber library differ from Boost.Fiber?

Helio's fb2 library keeps the non-intrusive, shared nothing design of Boost.Fiber but is more opinionated and adds full networking support for asynchronous multi-threaded execution. Helio fibers let you inject a custom util::fb2::DispatchPolicy into a dispatching fiber rather than requiring a separate polling fiber, and they use a lockless queue for inter-thread notification where Boost.Fiber required locking.

How do I build Helio and run the echo server example?

Run sudo ./install-dependencies.sh, then ./blaze.sh -release, then cd build-opt && ninja -j4 echo_server. You then run ./echo_server --logtostderr in one tab and ./echo_server --connect=localhost --n 100000 --c=4 in another.

What monitoring and profiling does the Helio HTTP server include?

HTTP is handled with Boost.Beast integrated with the io_uring-based ProactorPool, and it includes a Varz status page at localhost:8080 where the backend returns a JSON object rendered in the browser, an extensible debugging interface, and integrated CPU profiling with gperf-tools and pprof that can be triggered in production using magic-url commands.

How does Helio detect whether it is running on a cloud platform?

util/cloud/platform_info.h provides util::cloud::PlatformInfo::Create() for local cloud, hypervisor and deployment classification. Detection uses DMI, CPU flags, environment variables and runtime marker files without contacting metadata services, and the result keeps each source's io::Result alongside the classification enums.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. Project website
  4. README
  5. romange/helio on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/romange-helio.svg)](https://hysenlabs.com/projects/romange-helio)

Was this useful?