EDIT: Also IIRC when using select() writes can still block your thread...
EDIT: Also IIRC when using select() writes can still block your thread...
You should only ever use select() with file descriptors in non-blocking mode. Otherwise there's an inherent race condition with any readiness signaling mechanism along these lines. But yes, the read/write syscalls are not asynchronous, and select() on regular files are required by POSIX to always be signaled as ready for reading and writing. Basically, descriptor readiness as per select() does not mean and has never meant that the thread won't be temporarily descheduled and put on an IO wait queue. Its semantics are only a useful fit for things like UARTs, sockets, pipes, etc.
By the way, even with asynchronous/overlapped IO on Windows, there are cases where asynchronous reads/writes on files will effectively be synchronous, especially in the common case of file-extending writes. See https://support.microsoft.com/en-us/kb/156932 for details. The only work-around I know of is to use SetFileValidData to pre-extend the file, but that requires increased privileges and so is not often feasible (and usually not worth it compared to queuing the writes to a dedicated IO thread).
Whereas essentially all platforms (including Windows) have select() and most have something equivalent but better like epoll() or kqueue().
And for most applications the benefits of IOCP over epoll() or kqueue() are only theoretical. A call to send() or recv() isn't literally synchronous, it's buffered by the OS.
So using IOCP looks better on paper than in practice.
Between select/poll/epoll/kqueue/etc and each other that is easy. You have a few functions like "register(ctx, fd, event)" and "wait_for_events(ctx)" that map straight to each of the implementations.
To use IOCP it's not just those you have to wrap. It's also send(), sendto(), sendmsg(), write(), recv(), recvfrom(), recvmsg(), read(), connect(), accept() and others. In ways that don't make for trivial or efficient implementations. And then contend with any third party library that uses any of those internally.
There is a reason libev doesn't support IOCP and libevent only supports it by exposing a different API on Windows.
It is also telling, of how difficult to interconvert it actually is in practice, that the Heily libkqueue does not implement EVFILT_AIO, EVFILT_FS, or EVFILT_PROCDESC, and implements some of the others in only a limited fashion.
Granted libaio only really works well on XFS [3], and even there it has issues.
[0] - https://lwn.net/Articles/251413/
[1] - http://man7.org/linux/man-pages/man2/eventfd.2.html
[2] - https://www.fsl.cs.sunysb.edu/~vass/linux-aio.txt
[3] - http://www.scylladb.com/2016/02/09/qualifying-filesystems/