> epoll/kqueue are replacements for their deprecated counterparts poll and select.
Neither poll nor select are deprecated. They're just not good fits for particular use patterns. But even select() is fine if you just need to watch 2 FDs in a CLI tool.
In fact, due to its footguns, I'd highly advise against epoll (particularly edge triggering) unless you really need it.
> But even select() is fine if you just need to watch 2 FDs in a CLI tool.
Only if those fds are below ~1024 or whatever. (If you're going to use one of the legacy interfaces, at least poll() doesn't have arbitrary limits on the numeric value of the fd.)
The Winsock version doesn't have this limitation. It's a very weak select() because it only works on network sockets, but it doesn't care about numeric values of file descriptors. As on POSIX, file descriptors are added and removed to the select set using macros, and these work on a vector or linked list (I forgot) instead of a bitset.
lubuv, libevent introduce a layer of abstraction, their own approach to buffer mgmt, etc.
If poll() works, better stay with poll().
It is universally portable, things stay pretty clean and simple as a result.
Right now I am working on JavaScript bindings for a project and doing it the node.js way (or Deno) is definitely a no-no. That would be one more layer in the architecture, if not two. Once you have more layers, you also have more layer interactions and that never stops.
Very specific, but very common, such as a web server, forward proxy, reverse proxy, load balancer, etc. Used in high millions of instances. I'd say a toy CLI tool is a very specific usecase for a lot less people than the above.
More like a chat server or a sync server. Normal Web server connections are short lived. Flash crowds from HN would give 100K visits a day maybe, that is like 1 or 2 at a time unless you WebSocket them.
Imagine what sort of traffic you need to saturate 1000 conns with HTTP. Can your single thread app handle it? If you are not using nginx, that must be something less trivial than a reverse proxy. Nginx can do lots of things, by the way.
The man page also suggests how you might increase the FD limit if needed. I still use select for a small number of FDs where overhead isn't a real concern, and select is a good fit.
It is a very, very easy mistake to make though. Nothing except edge-triggered I/O multiplexing makes it a problem not to read everything you possibly could, and it is often convenient to read less and let some other part of the code handle the rest. Forgot to call that part somehow? Oops, I/O on that socket is now screwed forever.
You just read() until it returns EWOULDBLOCK/EAGAIN before calling epoll_wait again. It's no less valid than level-triggering as a default mental model.
It's valid, just very error-prone. An advantage of the readiness model of doing I/O over the IOCP/io_uring model is that you keep control over when, where and how much data you read. If you always have to read everything, that advantage is greatly reduced. You still have a little more control and easier memory management. Performance is generally worse vs IOCP - you should at least get some convenience for it!
Neither poll nor select are deprecated. They're just not good fits for particular use patterns. But even select() is fine if you just need to watch 2 FDs in a CLI tool.
In fact, due to its footguns, I'd highly advise against epoll (particularly edge triggering) unless you really need it.