Skip to content

select Module

The select module provides low-level I/O multiplexing primitives that wait for file descriptors to become ready for reading or writing.

Complexity Reference

Operation Time Space Notes
select.select() O(n) O(n) n = total fds passed; scans all sets
poll.register() / poll.unregister() / poll.modify() O(1) avg O(1) Per fd; Python bookkeeping + OS registration
poll.poll() O(n) O(k) n = registered fds, k = ready fds
epoll.register() / epoll.unregister() / epoll.modify() O(1) avg O(1) Per fd; OS-dependent
epoll.poll() O(k) typical O(k) k = ready fds; OS-dependent
kqueue.control() O(m + k) O(k) m = changelist length; k = ready events

Platform availability

poll, epoll, and kqueue are not available on all platforms. Always guard with hasattr(select, "poll"), hasattr(select, "epoll"), or hasattr(select, "kqueue").

Waiting with select()

import select
import socket

# Create a connected socket pair (cross-platform in Python 3.5+)
left, right = socket.socketpair()

# Send data so right becomes readable
left.sendall(b"ping")

# Wait for readability - O(n) where n = len(rlist) + len(wlist) + len(xlist)
rlist, wlist, xlist = select.select([right], [], [], 1.0)
if rlist:
    data = right.recv(4096)
    print(data)  # b'ping'

left.close()
right.close()

Polling with poll()

import select
import socket

if hasattr(select, "poll"):
    left, right = socket.socketpair()
    poller = select.poll()

    # Register for read events - O(1) average
    poller.register(right, select.POLLIN)

    left.sendall(b"hello")

    # Wait for events - O(n) for n registered fds
    events = poller.poll(1000)  # timeout in ms
    for fd, event in events:  # O(k) for k ready
        if event & select.POLLIN:
            print(right.recv(4096))  # b'hello'

    poller.unregister(right)  # O(1) average
    left.close()
    right.close()

Using epoll (Linux)

import select
import socket

if hasattr(select, "epoll"):
    left, right = socket.socketpair()
    ep = select.epoll()

    # Register read interest - O(1) average
    ep.register(right, select.EPOLLIN)

    left.sendall(b"event")

    # Wait for ready fds - typically O(k)
    events = ep.poll(1.0)
    for fd, event in events:
        if event & select.EPOLLIN:
            print(right.recv(4096))  # b'event'

    ep.unregister(right)  # O(1) average
    ep.close()
    left.close()
    right.close()

Using kqueue (BSD/macOS)

import select
import socket

if hasattr(select, "kqueue"):
    left, right = socket.socketpair()
    kq = select.kqueue()

    # Register a read event - O(1) average
    kev = select.kevent(right, filter=select.KQ_FILTER_READ, flags=select.KQ_EV_ADD)
    kq.control([kev], 0, 0)

    left.sendall(b"kq")

    # Wait for events - O(m + k) where m = changelist length
    events = kq.control([], 1, 1.0)
    if events:
        print(right.recv(4096))  # b'kq'

    kq.close()
    left.close()
    right.close()

Notes and Best Practices

select() scalability limits

select.select() must scan all fds each call and may be limited by the platform's FD set size. For large numbers of connections, prefer selectors or an epoll/kqueue-based approach.