libuv gives C one model across epoll, kqueue, and IOCP
libuv wraps epoll on Linux, kqueue on macOS and BSD systems, IOCP on Windows, and event ports on SunOS behind one C API. The event loop is the center, while handles represent long-lived objects such as TCP servers and requests represent shorter operations such as writes or DNS lookups. This makes it a runtime foundation rather than an application framework.
The libuv README lists TCP, UDP, DNS, file operations, file watching, terminals, IPC, child processes, signals, clocks, and synchronization primitives. It does not provide HTTP, TLS, or an application framework. Node.js, Julia, Luvit, and uvloop use it under higher-level APIs. The v1 line follows semantic versioning and promises a stable ABI across minor releases, which matters when bindings and system packages update on different schedules.
A 4.7 MB checkout supports CMake and autotools
commit e00b641 contained 483 files, about 116,675 source lines, and occupied 4.7 MB. Unix-like systems can build with autotools or CMake, while Windows uses CMake with Visual C++ tools. The repository documents Homebrew, vcpkg, and Conan paths too. It needs no account or daemon. Setup trouble usually appears in downstream native integration rather than an installer.
The test and benchmark programs double as examples. CMake can generate shared and static test executables, and a named test can run inside the driver for gdb or under a child process with fork-aware settings. The README recommends -fno-strict-aliasing because its C API uses an ad hoc inheritance pattern. That flag gets awkward when libuv arrives through several layers of native dependencies.
What happened when we ran it
Our sandbox installed commit e00b641 in 10 seconds and completed the build in 30 seconds. The unprivileged container had 3 CPUs, 8 GB of RAM, no secrets, and no Docker daemon. We counted nine CI workflow files and a tests directory, with no Dockerfile. Those results show that a normal C toolchain could produce the artifacts quickly in our environment. They do not measure network throughput, timer precision, or cross-platform behavior.
CTest failed with exit code 8 after 49 seconds, reporting 0 passed and 2 failed of 2 targets. The final log lines showed TAP cases 531 through 540 as ok, including UTF conversion, handle walking, file watching, and signal cases. That does not turn the CTest targets green. The supplied tail omits the earlier failure line or final target-level reason, so we cannot say whether timing, environment, or a particular assertion caused the result.
The event loop belongs to one thread
The design guide says an event loop is tied to a single thread and that loop or handle APIs are not thread-safe unless documented otherwise. Network I/O remains on the loop thread. File-system work, selected DNS calls, and user work submitted through uv_queue_work use a global thread pool. This model requires disciplined ownership when a runtime has several loops or worker threads.
Resource cleanup is equally explicit. A successfully initialized handle must eventually reach uv_close, and its memory cannot be reused until the close callback runs. Requests usually clean themselves up, with named exceptions for file-system and address-resolution requests. A binding must translate those lifecycle rules correctly. A garbage collector does not know that a native handle is still waiting for its close callback.
Tier 1 covers three major desktop families
The supported-platform matrix gives Tier 1 status to GNU/Linux 3.10 or newer with glibc 2.17, macOS 11 or newer, and Windows 10 or newer. FreeBSD, AIX, IBM i, z/OS, and musl Linux are Tier 2. Android and MinGW are Tier 3, where community maintainers may need to repair accidental breaks.
The README notes that some tests are timing-sensitive and lets slow machines increase timeouts with UV_TEST_TIMEOUT_MULTIPLIER. That knob is useful when a host is overloaded. It should not erase a repeatable failure without finding the responsible case. Our run had a 45.93-second real CTest time inside the reported 49-second step, yet the visible tail only showed successful individual cases. Preserve the full log in CI so the first failing line is not lost.
v1.53.0 is active, and small changes reach runtimes
v1.53.0 was released on September 24, 2026, and the repository was pushed on September 30. GitHub showed 27,220 stars and 240 combined issues and pull requests. The release addressed Windows pipes and sockets, Unix process creation, file systems, UDP handling, documentation, and test races. Ordinary-looking changes here sit between operating systems and programs with different runtime models.
Open issue #5313 reports an extra observable SIGCHLD after a process-creation change between v1.52.1 and v1.53.0. The report includes a C reproducer and describes the effect on a downstream runtime. Active discussion and a quick follow-up pull request are healthy signs. They also show why ABI stability alone is insufficient: behavior around signals, cancellation, pipes, and process spawning still needs application-level regression coverage.
libuv is a strong choice for authors who genuinely need a portable C event loop and accept its callback and lifetime model. It is excessive for an application that only wants an HTTP client, and Asio will feel more natural in a modern C++ codebase. Our two failed CTest targets stop short of disqualifying a project this established, but they do require a rerun with complete logs before shipping the exact revision we tested.

