Skip to main content

Modules/selectmodule.c (part 8)

Source:

cpython 3.14 @ ab2d84fe1023/Modules/selectmodule.c

This annotation covers the select module's I/O multiplexing interfaces. See modules_select7_detail for select.devpoll and the file descriptor abstraction.

Map

LinesSymbolRole
1-80select.selectPOSIX select(2) wrapper
81-180select.pollpoll(2) object — register/unregister/poll
181-280select.epollLinux epoll interface
281-380select.kqueueBSD kqueue interface
381-500FD abstractionfileno() dispatch for sockets and files

Reading

select.select

// CPython: Modules/selectmodule.c:320 select_select_impl
static PyObject *
select_select_impl(PyObject *module, PyObject *iwtd, PyObject *owtd,
PyObject *ewtd, PyObject *timeout_obj)
{
fd_set ifd, ofd, efd;
struct timeval tv, *tvp = NULL;
/* Convert Python lists to fd_sets */
...
Py_BEGIN_ALLOW_THREADS
n = select(maxfd + 1, &ifd, &ofd, &efd, tvp);
Py_END_ALLOW_THREADS
if (n < 0) return PyErr_SetFromErrno(PyExc_OSError);
/* Build result lists from set bits */
return Py_BuildValue("(NNN)", rlist, wlist, xlist);
}

select.select(rlist, wlist, xlist, timeout) wraps the POSIX select syscall. It accepts any file-like objects with fileno(). The GIL is released during the blocking call.

select.poll

// CPython: Modules/selectmodule.c:560 poll_poll
static PyObject *
poll_poll(pollObject *self, PyObject *args)
{
struct pollfd *ufds = self->ufds;
int timeout = -1; /* block forever by default */
...
Py_BEGIN_ALLOW_THREADS
retval = poll(ufds, self->ufd_len, timeout);
Py_END_ALLOW_THREADS
if (retval < 0) return PyErr_SetFromErrno(PyExc_OSError);
/* Build list of (fd, event) pairs */
}

poll is more scalable than select for large numbers of fds since it uses a linear array rather than a fixed-size bitset. The registered fds are stored in a C pollfd array rebuilt each time poll() is called.

select.epoll

// CPython: Modules/selectmodule.c:880 pyepoll_poll
static PyObject *
pyepoll_poll(pyEpoll_Object *self, PyObject *args, PyObject *kwds)
{
int nfds = -1;
double timeout = -1.0;
struct epoll_event *evs = alloca(nfds * sizeof(struct epoll_event));
Py_BEGIN_ALLOW_THREADS
nready = epoll_wait(self->epfd, evs, nfds, (int)(timeout * 1000));
Py_END_ALLOW_THREADS
/* Build list of (fd, event) tuples */
}

epoll is O(1) per event rather than O(n) for the entire fd set. EPOLLET (edge-triggered) mode only reports events on transitions. Used by asyncio on Linux.

gopy notes

select.select is module/select.Select in module/select/module.go using syscall.Select. select.poll uses syscall.Poll. select.epoll is module/select.Epoll backed by Go's golang.org/x/sys/unix.EpollWait. kqueue is only available on Darwin/BSD.