In this lesson, you will learn how the Rust runtime bridges the gap between high-level Future tasks and low-level operating system notification mechanisms.
The Reactor-Executor Divide
In our previous modules, we discussed how the Executor manages the polling of Future states. However, an Executor shouldn't busy-wait for I/O; that would defeat the purpose of cooperative multitasking. This is where the Reactor comes in. The Reactor acts as the "waiter" for the entire system, sitting between the Rust Future and the OS kernel.
Interfacing with OS Primitives
When a Future performs an I/O operation (like reading a socket) and isn't ready, it returns Poll::Pending. Crucially, before it yields, it registers its Waker with the Reactor.
The Reactor manages the OS-specific notification primitives:
- epoll (Linux): The Reactor maintains an
epollfile descriptor. It adds interest flags (e.g.,EPOLLIN) for sockets that the executor is waiting on. - kqueue (BSD/macOS): Similar to
epoll, the Reactor registers events with the kernel to be notified when a file descriptor becomes readable or writable. - io_uring (Modern Linux): The Reactor manages submission and completion queues, allowing the kernel to perform the I/O asynchronously and update the memory ring buffer directly.
The Integration Flow
Consider this simplified interaction loop:
- Poll: The
Executorpolls aFuture. - Register: The
Futureattempts I/O, fails, and the Reactor adds the file descriptor to the OS interest list (epoll_ctl). - Wait: The Reactor calls
epoll_wait, blocking the runtime thread efficiently until the OS signals activity. - Wake: When the OS triggers an event, the Reactor identifies the associated
Wakerand callswaker.wake(). - Re-poll: The
Executoris notified that the task is ready and polls theFutureagain to finish the I/O operation.
By offloading the "waiting" to the kernel, the Reactor ensures the event loop only spends CPU cycles when there is actual work to be done, preventing the thread from spinning pointlessly.
Key Takeaway: The Reactor pattern decouples task execution from I/O readiness by mapping Future Wakers to kernel event notifications like epoll, kqueue, or io_uring.