Curious difference between epoll, poll, kqueue and select
Suppose you have a brand new nonblocking socket and you want to do something with it (i have a connect in mind – but it’s irrelevant anyway). So what is your obvious choice for that since the socket is non-blocking ? Use select or poll or whatever ofcourse. Right? NO.
Sadly they don’t work the same with unconnected sockets – i wonder why.
select (win32):
>>> import select, socket >>> s = socket.socket() >>> select.select([s], [s], [s], 1) ([], [], [])
select (linux):
>>> import select, socket >>> s = socket.socket() >>> select.select([s], [s], [s], 1) ([<socket._socketobject>], [<socket._socketobject>], [])
select (freebsd):
>>> import select, socket >>> s = socket.socket() >>> select.select([s], [s], [s], 1) ([], [], [])
Pretty inconsistent eh?poll (linux):
>>> import select, socket >>> poll = select.poll() >>> s = socket.socket() >>> poll.register(s.fileno(), select.POLLIN | select.POLLOUT) >>> (fd, ev), = poll.poll(1000) >>> ev & select.POLLIN 0 >>> ev & select.POLLOUT 4 >>> ev & select.POLLERR 0 >>> ev & select.POLLHUP 16
poll (freebsd):
>>> import select, socket >>> poll = select.poll() >>> s = socket.socket() >>> poll.register(s.fileno(), select.POLLIN | select.POLLOUT) >>> (fd, ev), = poll.poll(1000) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueErr1r: need more than 0 values to unpack
Oh NOES!!!!1 It’s not portable.epoll:
>>> import epoll, socket >>> efd = epoll.epoll_create(16) >>> s = socket.socket() >>> epoll.epoll_ctl(efd, epoll.EPOLL_CTL_ADD, s.fileno(), epoll.EPOLLIN | epoll.EPOLLOUT) 0 >>> (ev, fd), = epoll.epoll_wait(efd, 16, 1000) >>> ev & epoll.EPOLLHUP 16 >>> ev & epoll.EPOLLOUT 4
>>> import kqueue, socket >>> kq = kqueue.kqueue() >>> s = socket.socket() >>> kq.kevent(kqueue.EV_SET(s.fileno(), kqueue.EVFILT_READ | kqueue.EVFILT_WRITE, kqueue.EV_ADD | kqueue.EV_ENABLE)) >>> kq.kevent(None, 16, 1000000000) []
I wonder why are these so inconsistent. One would expect come consistent results for multiplexing a simple thing such a unconnected brand new socket, no?If you think that is horrible – well, i do – check out the tricks to check for connection success on non-blocking sockets – and they don’t cover win32:
However, win32 has a much nicer api in the so called overlapped io wich is very different: you don’t need to check the sockets before making a operation – you just make the socket call with a overlapped and check for for completed calls (via io completion ports – gqcs). Still, you can use some selects variants (WSAAsyncSelect, WSAEventSelect, select) wich are just as bad.
Unfortunately python has a incomplete binding, pywin32 (not that bad – only ConnectEx and TransmitFile missing) for that and it’s actually fairly easy to crash your python interpreter with this.
If you tell us what the point of callinh select or epoll on an unconnected stream socket is (which is what you seem to be trying to d), we might be concerned.
While there are some differences between all those interfaces, they work pretty consistent if you actually do useful stuff, such as non-blocking connects, accepts or i/o.
Otherwise, it’s just garbage in garbage out.
(overlapped I/O is a lot less consistent btw., and requires you to tie down a lot of resources. It is hardly an improvement over select)
Checking unconnected sockets for readiness is useful when you want to accept/connect with that (non-blocking) socket.
It’s incosistent because the different calls appeared on different platforms in different time. I can see how you sit today with windows, linux AND bsd and go “why so many different calls”, but those operating systems appeared at different times, not to mention the myriad of various unix “flavors” and two major differences between them (bsd/system V).
Also, event loop calls like epoll/kqueue are pretty recent additions which address the now classic question of “how do you write server that handles 10.000 simultaneous connections”. Back in the days, that wasn’t the issue, because everything was smaller and less used. People never could imagine running out of space in IPv4 address space, or running out of port numbers (16-bit).
However, today, with internet around and new applications like telecom, I found myself recently writing code thats trying to establish one million tcp connections, on a single machine, on linux.
There was a lot of kernel hacking required to make it possible, and all the old cruft like select() and poll() are pretty much useless there, because they just simply don’t scale.