Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> 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.


We’re talking about Linux/POSIX here.


1024 is the default fd limit anyway, isn't it?


...because of select, and how it became essentially impossible to ensure that a process wasn't using it anywhere, indirectly or incidentally.


or better yet, go with libevent (https://libevent.org, almost) always better than ’naked’ calls to low level routines, and cross-platform to boot.


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.

I mean, complexity begets complexity

https://github.com/gritzko/librdx/blob/master/js/README.md

Having more than 1000 conns per a thread is a very specific usecase.


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.


select() is at least kind of deprecated, in that its own man page says not to use it in new code.


I don't see it in the man page?

https://man.freebsd.org/cgi/man.cgi?select

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.



In anything new you should use poll not select.

They're basically identical apis but poll doesn't have a hard limit and works with high number fds.


Doesn't seem super relevant when the program will only have 5 FDs. Safe signal handling in poll does seem handy though.


Your program can be executed with fds 0-1023 already open.


What's the footgun with edge triggering?


The edge in epoll edge triggering is going from has data to doesn't have data.

So the obvious loop using level triggering switched to edge will eventually lock up.

You'll read 4092bbytesbwhen there is 4093 bytes leaving 1 behind and then never get a signal again.


This is a blatant application bug, not an epoll issue, unless you prove otherwise.


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!


select() is great for embedded daemons and user space signals handling, and so on.

Just don't try to solve the 10,000x problem with it, by putting it on the Internet.

Or, if you do, build it out properly.

Or use epoll or kqueue.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: