IOCP and POSIX AIO
/ IOCP -> EYE-oh-see-pee; AIO -> AY-eye-oh /
Long before io_uring, two other systems offered completion-model I/O - 'do the transfer for me and tell me when it is done.' On Windows that is IOCP, the I/O completion port, the backbone of every serious Windows server. On Unix the POSIX standard defines POSIX AIO, an interface that looks asynchronous on paper but is much weaker in practice. Both belong in the completion-model family alongside io_uring.
IOCP works like a shared loading dock with a small crew. You associate sockets and file handles with a completion port, then start overlapped (asynchronous) operations such as WSARecv or ReadFile that return immediately while the OS does the actual transfer in the background. A small fixed pool of worker threads calls GetQueuedCompletionStatus(), which blocks until a finished operation is posted to the port, then hands that thread the completed result. The kernel itself balances how many threads are runnable, so a handful of threads efficiently serve tens of thousands of connections - this is Windows' native answer to C10k. POSIX AIO offers aio_read() and aio_write(): you submit a struct aiocb and are later notified by signal or callback. But on Linux it is historically implemented in user space with helper threads rather than in the kernel, covers mainly regular files, and is widely seen as disappointing - which is a large part of why io_uring was created.
Why it matters: knowing these rounds out the completion model. The reactor-versus-proactor distinction maps directly onto platforms - reactor (readiness) suits epoll and kqueue, while proactor (completion) suits IOCP and io_uring. Portable async libraries (libuv, ASIO, Tokio) hide all of this, presenting one interface over epoll on Linux, kqueue on macOS, and IOCP on Windows, so most application code never calls these APIs directly.
/* Windows IOCP, sketch */ HANDLE port = CreateIoCompletionPort(sock, NULL, key, 0); WSARecv(sock, &wsabuf, 1, NULL, &flags, &overlapped, NULL); /* worker: */ GetQueuedCompletionStatus(port, &bytes, &key, &ov, INFINITE);
On Windows you start an overlapped recv and a worker thread later collects the completion from the port.
POSIX AIO is not the same as io_uring and is not where modern Linux async I/O lives - on Linux it is typically a user-space thread-pool shim limited to files, so do not reach for it for scalable networking. IOCP, by contrast, is genuinely kernel-level and is the real model for high-performance Windows I/O.