-
Notifications
You must be signed in to change notification settings - Fork 9
Description
Using Unix.select is problematic, because if any file descriptor value is beyond 1024 then the call will fail with an exception.
Note that this refers to the value of the file descriptor, and not the number of file descriptors that select is watching: you could fail even if you only ever watch a single file descriptor, if you open that file descriptor late enough that it gets a number >1024.
This is not a concern for short-lived programs, but can be a real problem for long-lived daemons.
It'd be better to use poll(3p), which is part of POSIX.1-2017 and available nearly everywhere.
This is available in the iomux library.
Unfortunately Unix.select in the stdlib cannot be efficiently reimplemented using poll, because it has a different interface, but we shouldn't encourage more uses of select, other than for compatibility with legacy applications (I am currently working on removing all uses of Unix.select from such a legacy application...)
I know a project that has replaced select with poll in an application by writing a drop-in replacement that kept the same signature, and it was a very bad idea for performance: it allocates a lot, and it performs O(watched-fds) work every time.
More efficient OS specific implementations are also possible:
-
epoll,kqueue,wepoll(iomuxclaims it'll eventually support these, but doesn't yet), available in:
https://ocaml.org/p/poll/latest -
io_submitwithIOCB_CMD_POLLhttps://ocaml.org/p/aio/latest/doc/Aio/index.html#val-poll
This one also has the potential to check for the presence of events without making any system calls. https://spdk.io/ has such a pure userspaceio_geteventsimplementation. (of course to wait for an event when there are none available you have to make the actual system call) -
io_uring, as implemented by the uring package
As you can see there are plenty of choices, and the interfaces are quite distinct, and most of these are modelled more after a poll-like interface than a select-like interface. It is probably out of scope for this project to unify all these mechanisms, but it'd be good if the default is something better than select.
A neutral approach might be to use poll by default, and provide the ability for the user to override the implementation (via a functor, at main thread configure time, or via a settable global, etc.).
A picos_select.core functor with an implementation in picos_select.iomux might provide the most flexibility:
- you get an acceptable default that is available on a lot of platforms
- if for some reason you don't want to depend on
iomuxin your application, you can choose not to, by linking withpicos_select.coreand providing your own implementation for the poll signature. - for backwards compat
picos_selectcould pickiomuxby default
(There might be other ways, I haven't looked at how Dune's virtual libraries could be used for this).
If you want I can attempt to open a draft PR with a proof of concept, but thought to open an issue describing the problem first, and maybe you already have a design on how you'd like this handled.