2026-08-28
Before eventfd() (Linux 2.6.22), waking a thread that was blocked in epoll_wait() from another thread required awkward tricks: a self-pipe (write one byte, drain the other end), a socketpair, or the notorious signal-and-hope pattern. eventfd() replaces all of these with a single file descriptor backed by an 8-byte kernel counter.
The mechanics are small and sharp. eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC) returns an fd. A write() of a uint64_t value adds that value to the internal counter. A read() returns the current counter value and (in default semantics) resets it to zero. The fd is "readable" (from select/poll/epoll's perspective) whenever the counter is non-zero. Total kernel-side cost: one atomic counter and a wait queue — no pipe buffer, no socket state.
Two flags change the semantics substantially:
read() returns 1 and decrements the counter by 1, blocking if it's zero. Now it behaves exactly like a POSIX semaphore, but one you can register with epoll.read() returns EAGAIN instead of blocking. Mandatory for use inside event loops.Real-world example — waking a reactor thread. Suppose you have a networking daemon whose main thread is blocked in epoll_wait(-1). A worker thread finishes a background job and wants to enqueue a result. It pushes onto a lock-free queue, then does write(evfd, &one, 8). The main thread wakes, reads the counter (draining all pending notifications in one syscall — 500 writes coalesce into one read of 500), and drains the queue. This coalescing property is why eventfd beats a pipe for high-frequency wakeups: a pipe fills up after 65KB and the writer blocks; the eventfd counter needs 2⁶⁴ − 1 wakeups before write() returns EAGAIN.
Rule of thumb: If your notification is "something happened, come look," use default mode — one read() handles any batch size. If your notification represents units of work that must each be consumed individually, use EFD_SEMAPHORE. Never use a pipe for cross-thread wakeups in new code.
KVM uses eventfd extensively via irqfd and ioeventfd: the host kernel raises a guest interrupt or notifies a vhost worker by triggering an eventfd, avoiding a VM exit into userspace QEMU. Same primitive, different consumer.
eventfd() is a wait-queue plus an 8-byte counter exposed as a file descriptor — the cheapest way to wake an epoll loop from another thread or the kernel, with automatic coalescing that a pipe can't match.
