Understanding OS-Backed Event Queues, System Calls, and Cross-Platform Abstractions 50 Lending out a buffer to the OS also provides some challenges since it's very important that this buffer stays untouched while waiting for an operation to return. Windows Linux mac OS IOCP epoll kqueue Completion based Readiness based Readiness based Table 3. 1-Different platforms and event queues Cross-platform event queues When creating a cross-platform event queue, you have to deal with the fact that you have to create one unified API that's the same whether it's used on Windows (IOCP), mac OS (kqueue), or Linux (epoll). The most obvious difference is that IOCP is completion-based while kqueue and epoll are readiness-based. This fundamental difference means that you have to make a choice: Y ou can create an abstraction that treats kqueue and epoll as completion-based queues, or Y ou can create an abstraction that treats IOCP as a readiness-based queue From my personal experience, it's a lot easier to create an abstraction that mimics a completion-based queue and handle the fact that kqueue and epoll are readiness-based behind the scenes than the other way around. The use of wepoll, as I alluded to earlier, is one way of creating a readiness-based queue on Windows. It will simplify creating such an API greatly, but we'll leave that out for now because it's less well known and not an approach that's officially documented by Microsoft. Since IOCP is completion-based, it needs a buffer to read data into since it returns when data is read into that buffer. Kqueue and epoll, on the other hand, don't require that. They'll only return when you can read data into a buffer without blocking. By requiring the user to supply a buffer of their preferred size to our API, we let the user control how they want to manage their memory. The user defines the size of the buffers, and the re-usages and controls all the aspects of the memory that will be passed to the OS when using IOCP. In the case of epoll and kqueue in such an API, you can simply call read for the user and fill the same buffers, making it appear to the user that the API is completion-based.