libuv
The cross-platform async I/O library extracted from Node.js.
What it is
libuv is a multi-platform C library that provides asynchronous I/O, event loops, timers, and cross-platform abstractions for networking, filesystems, and concurrency. It is designed to support high-performance, scalable applications.
libuv allows developers to handle TCP/UDP sockets, files, and timers asynchronously using an event loop. It supports callbacks, handles, streams, and integrates well with C/C++ applications requiring high concurrency.
- Licence
- MIT
- Powers
- Node.js, Julia and Neovim
When to use it
The question documentation cannot answer for you — because it cannot recommend something else.
Reach for it when
- Building an event-driven server or runtime in C
- You need one abstraction over epoll, kqueue and IOCP
Look elsewhere when
- A C++ project that could use Boost.Asio's higher-level, type-safe interface
Installation
sudo apt install libuv1-devGetting started
The smallest useful thing you can do with it, and what each part means.
#include <uv.h>
#include <stdio.h>
int main() {
uv_loop_t *loop = uv_default_loop();
printf("Loop started\n");
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
return 0;
}#include <uv.h>
#include <stdio.h>
void timer_cb(uv_timer_t* handle) {
printf("Timer fired!\n");
}
int main() {
uv_loop_t *loop = uv_default_loop();
uv_timer_t timer_req;
uv_timer_init(loop, &timer_req);
uv_timer_start(&timer_req, timer_cb, 1000, 0); // Fire once after 1000ms
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
return 0;
}Advanced usage
Where the library earns its place over a simpler alternative.
#include <uv.h>
#include <stdio.h>
void on_new_connection(uv_stream_t *server, int status) {
if (status < 0) {
fprintf(stderr, "New connection error: %s\n", uv_strerror(status));
return;
}
printf("New client connected\n");
}
int main() {
uv_loop_t *loop = uv_default_loop();
uv_tcp_t server;
uv_tcp_init(loop, &server);
struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", 7000, &addr);
uv_tcp_bind(&server, (const struct sockaddr*)&addr, 0);
int r = uv_listen((uv_stream_t*)&server, 128, on_new_connection);
if (r) {
fprintf(stderr, "Listen error: %s\n", uv_strerror(r));
return 1;
}
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
return 0;
}#include <uv.h>
#include <stdio.h>
#include <stdlib.h>
void read_cb(uv_fs_t* req) {
printf("File content: %s\n", (char*)req->bufs->base);
uv_fs_req_cleanup(req);
}
int main() {
uv_loop_t *loop = uv_default_loop();
uv_fs_t open_req;
uv_fs_open(loop, &open_req, "test.txt", O_RDONLY, 0, NULL);
uv_fs_t read_req;
char *buffer = malloc(1024);
uv_buf_t iov = uv_buf_init(buffer, 1024);
uv_fs_read(loop, &read_req, open_req.result, &iov, 1, 0, read_cb);
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
free(buffer);
return 0;
}#include <uv.h>
#include <stdio.h>
void alloc_buffer(uv_handle_t *handle, size_t suggested_size, uv_buf_t *buf) {
buf->base = malloc(suggested_size);
buf->len = suggested_size;
}
void on_read(uv_udp_t *req, ssize_t nread, const uv_buf_t *buf, const struct sockaddr *addr, unsigned flags) {
if (nread > 0) printf("Received: %s\n", buf->base);
free(buf->base);
}
int main() {
uv_loop_t *loop = uv_default_loop();
uv_udp_t recv_socket;
uv_udp_init(loop, &recv_socket);
struct sockaddr_in addr;
uv_ip4_addr("0.0.0.0", 7000, &addr);
uv_udp_bind(&recv_socket, (const struct sockaddr*)&addr, 0);
uv_udp_recv_start(&recv_socket, alloc_buffer, on_read);
uv_run(loop, UV_RUN_DEFAULT);
uv_loop_close(loop);
return 0;
}Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- UV_EADDRINUSE
- Occurs when trying to bind to an address/port already in use. Choose a free port or close conflicting applications.
- UV_ENOENT
- File or directory not found. Verify paths for filesystem operations.
- UV_ECONNREFUSED
- Connection refused on TCP/UDP. Check that the server is running and reachable.
Best practices
- Always initialize and close the event loop properly.
- Free memory for buffers and handles to avoid leaks.
- Use callbacks to handle events asynchronously.
- Leverage libuv timers and async handles for concurrency instead of threads.
- Handle errors in callbacks to prevent crashing the loop.
Alternatives
Comparable options, and the reason you would pick one over the other.
Background
Why it exists, and what it was reacting to.
libuv was originally developed as part of Node.js to provide a consistent, event-driven, asynchronous I/O layer across different platforms. It is now used in many other projects that require efficient non-blocking I/O, including networking applications, servers, and frameworks.
