# libxev libxev is a cross-platform event loop. libxev provides a unified event loop abstraction for non-blocking IO, timers, signals, events, and more that works on macOS, Windows, Linux, and WebAssembly (browser and WASI). It is written in [Zig](https://ziglang.org/) but exports a C-compatible API (which further makes it compatible with any language out there that can communicate with C APIs). **Project Status: 🐲 Unstable, alpha-ish quality.** The feature list is quite good across multiple platforms, but there are plenty of missing features. The project hasn't been well tested in real-world environments and there are lots of low-hanging fruit for performance optimization. I'm not promising any API compatibility at this point, either. If you want a production ready, high quality, generalized event loop implementation check out [libuv](https://libuv.org/), libev, etc. **Why a new event loop library?** A few reasons. One, I think Zig lacks a generalized event loop comparable to libuv in features ("generalized" being a key word here). Two, I wanted to build a library like this around the design patterns of [io_uring](https://unixism.net/loti/what_is_io_uring.html), even mimicking its style on top of other OS primitives ( [credit to this awesome blog post](https://tigerbeetle.com/blog/a-friendly-abstraction-over-iouring-and-kqueue/)). Three, I wanted an event loop library that could build to WebAssembly (both WASI and freestanding) and that didn't really fit well into the goals of API style of existing libraries without bringing in something super heavy like Emscripten. The motivation for this library primarily though is scratching my own itch! ## Features **Cross-platform.** Linux (`io_uring` and `epoll`), macOS (`kqueue`), WebAssembly + WASI (`poll_oneoff`, threaded and non-threaded runtimes). (Windows support is planned and coming soon) **[Proactor API](https://en.wikipedia.org/wiki/Proactor_pattern).** Work is submitted to the libxev event loop and the caller is notified of work _completion_, as opposed to work _readiness_. **Zero runtime allocations.** This helps make runtime performance more predictable and makes libxev well suited for embedded environments. **Timers, TCP, UDP, Files, Processes.** High-level platform-agnostic APIs for interacting with timers, TCP/UDP sockets, files, processes, and more. For platforms that don't support async IO, the file operations are automatically scheduled to a thread pool. **Generic Thread Pool (Optional).** You can create a generic thread pool, configure its resource utilization, and use this to perform custom background tasks. The thread pool is used by some backends to do non-blocking tasks that don't have reliable non-blocking APIs (such as local file operations with `kqueue`). The thread pool can be shared across multiple threads and event loops to optimize resource utilization. **Low-level and High-Level API.** The high-level API is platform-agnostic but has some opinionated behavior and limited flexibility. The high-level API is recommended but the low-level API is always an available escape hatch. The low-level API is platform-specific and provides a mechanism for libxev users to squeeze out maximum performance. The low-level API is _just enough abstraction_ above the OS interface to make it easier to use without sacrificing noticable performance. **Tree Shaking (Zig).** This is a feature of Zig, but substantially benefits libraries such as libxev. Zig will only include function calls and features that you actually use. If you don't use a particular kind of high-level watcher (such as UDP sockets), then the functionality related to that abstraction is not compiled into your final binary at all. This lets libxev support optional "nice-to-have" functionality that may be considered "bloat" in some cases, but the end user doesn't have to pay for it. **Dependency-free.** libxev has no dependencies other than the built-in OS APIs at runtime. The C library depends on libc. This makes it very easy to cross-compile. ### Roadmap There are plenty of missing features that I still want to add: * Pipe high-level API * Signal handlers * Filesystem events * Windows backend * Freestanding WebAssembly support via an external event loop (i.e. the browser) And more... ### Performance There is plenty of room for performance improvements, and I want to be fully clear that I haven't done a lot of optimization work. Still, performance is looking good. I've tried to port many of [libuv benchmarks](https://github.com/libuv/libuv) to use the libxev API. I won't post specific benchmark results until I have a better environment to run them in. As a _very broad generalization_, you shouldn't notice a slowdown using libxev compared to other major event loops. This may differ on a feature-by-feature basis, and if you can show really poor performance in an issue I'm interested in resolving it! ## Example The example below shows an identical program written in Zig and in C that uses libxev to run a single 5s timer. This is almost silly how simple it is but is meant to just convey the overall feel of the library rather than a practical use case.
Zig | C |
```zig const xev = @import("xev"); pub fn main() !void { var loop = try xev.Loop.init(.{}); defer loop.deinit(); const w = try xev.Timer.init(); defer w.deinit(); // 5s timer var c: xev.Completion = undefined; w.run(&loop, &c, 5000, void, null, &timerCallback); try loop.run(.until_done); } fn timerCallback( userdata: ?*void, loop: *xev.Loop, c: *xev.Completion, result: xev.Timer.RunError!void, ) xev.CallbackAction { _ = userdata; _ = loop; _ = c; _ = result catch unreachable; return .disarm; } ``` |
```zig
#include |