BSD kqueue is a mountain of technical debt (2021)

A blog post arguing that BSD’s kqueue event system is a “mountain of technical debt” compared to Linux’s epoll sparks a broader examination of how modern kernels should expose asynchronous I/O. Commenters largely find the technical-debt claim unsubstantiated, instead contrasting kqueue’s fixed set of event filters with epoll’s “everything is a file descriptor” model and pointing out serious quirks and pitfalls in epoll itself. The exchange widens into concerns about the lack of cross‑platform consensus on async I/O primitives, the complexity this creates for language runtimes, and whether newer mechanisms like io_uring or Windows-style I/O rings better match today’s performance and security constraints.

Overall view of the article and “technical debt” claim

  • Many commenters say the piece asserts that kqueue is “technical debt” without actually demonstrating it.
  • Several feel the headline is clickbait or overdramatic; the body reads more like a neutral history/comparison than proof of a “mountain of debt.”
  • Multiple people argue “technical debt” is misused and has become almost meaningless in this context.

kqueue vs epoll: design, extensibility, and composability

  • One side: epoll’s fd-centric model is seen as more composable:
    • New kernel objects just expose a file descriptor and automatically work with epoll, poll, select, and future event APIs.
    • You can pass fds between processes, read structured event data via read(), close them to free resources, and reuse generic tooling.
  • Counterpoint: kqueue’s filter model is also generic:
    • Drivers implement filters like EVFILT_READ/WRITE once, and BSDs even layer poll/select on top of kqueue internally.
    • New event types can either define new filters or reuse existing ones; no inherent need to grow kqueue itself.
    • Non-fd events (proc, timer, signal) are simpler in kqueue than Linux’s proliferation of *fd APIs.
  • Some note that Linux needed timerfd, signalfd, etc. specifically to make those things epoll-able, which others call “ugly” or its own form of debt.

Bugs, pitfalls, and “brokenness”

  • Linked critiques of epoll highlight:
    • Edge-triggered behavior that’s easy to misuse.
    • Confusing semantics around file descriptors vs open file descriptions, including events leaking across process boundaries and continuing for fds you no longer hold.
  • Others list signalfd and pidfd as messy or non-capability-based, contrasting with BSD process descriptors.

Async vs sync I/O and portability

  • Broad agreement that async I/O APIs are fragmented and hard to use portably; runtimes (libuv, language async runtimes) pay the price.
  • Debate over sync vs async:
    • Some argue synchronous I/O and threads suffice for most workloads and are easier to reason about.
    • Others counter that full-duplex protocols and high-concurrency servers push you toward async/event-driven models.

io_uring and “modern reality”

  • io_uring is discussed as a newer, ring-buffered, completion-based design:
    • Intended to reduce syscall overhead, which matters more on modern hardware and post-Spectre/Meltdown mitigations.
    • Seen as powerful but complex and security-prone; some hardened distros reportedly disable it.