std/event
The OS readiness poller behind the scheduler — kqueue on macOS, epoll on Linux, IOCP on Windows — plus the non-blocking fd flag every one of them requires.
from "std/event" import { EventLoop, eventLoopClose, setNonblocking, eventRegisterRead, eventPoll }This is the lowest layer of Milo's async I/O. std/runtime owns an EventLoop and drives it for you, so importing this module means you are writing a primitive the stdlib does not already provide — a poller over fds no std type wraps, or an integration with a C library's own descriptor.
std/event is a platform split: std/event.darwin.milo, std/event.linux.milo and std/event.windows.milo all export the same names and the resolver picks by target OS. Write from "std/event", never the suffixed path.
Polling
Set an fd non-blocking before registering it. Registering one that still blocks is the usual cause of a poller that reports ready and then hangs in read: readiness says a byte was available, not that the next read will return.
eventPoll fills readyFds, a caller-allocated [i32; N] with N >= maxEvents passed bare, and returns how many fds are ready, or -1 on error. A negative timeoutMs blocks indefinitely.
Every call here that takes a descriptor number is @unsafe: the poller holds the number, not the handle, so the caller vouches that its owner keeps it open while registered.
let fd: i32 = 0 // stdin, or any descriptor you own
var el = EventLoop.new()!
unsafe {
setNonblocking(fd)
eventRegisterRead(el, fd)
}
var ready: [i32; 8] = [0; 8]
// @unsafe: eventPoll writes up to maxEvents fds into `ready`, so it must hold that many.
var n: i32 = 0
unsafe { n = eventPoll(el, ready, 8, 1000) }
eventLoopClose(el)Wakeups and embedding
eventLoopInitWakeup arms a self-pipe (or its platform equivalent) that breaks a blocked eventPoll from elsewhere: that is how a Promise.blocking worker tells the scheduler its result is ready. eventLoopFd and eventLoopFromFd hand Milo's poller to another runtime, or wrap one that runtime already owns.
API reference
std/event has one implementation per target (darwin, linux, windows); the resolver picks the one for the target you build for.
EventLoop
pub struct EventLoopA kqueue/epoll/IOCP poller handle. Create one with EventLoop.new(); release it with eventLoopClose.
EventLoop.new
fn EventLoop.new(): Result<EventLoop, string>Create a poller. Errs if the kernel will not hand one out.
Functions
clearNonblocking
pub fn clearNonblocking(fd: i32): i32Clear O_NONBLOCK. fd 0/1/2 share an open file description with the parent shell, so a non-blocking flag left set here leaks out and makes the shell's reads return EAGAIN after we exit. Restore blocking mode before exiting.
efdCloexec
pub fn efdCloexec(): i32Only on linux.
efdNonblock
pub fn efdNonblock(): i32Only on linux.
epollEventSize
pub fn epollEventSize(): i64Only on linux.
eventDeregister
pub fn eventDeregister(el: &EventLoop, fd: i32, forWrite: bool): i32 // darwin, windows
pub fn eventDeregister(el: &EventLoop, fd: i32, _forWrite: bool): i32 // linuxStop watching fd. forWrite picks the write registration rather than the read one.
eventLoopClose
pub fn eventLoopClose(el: &EventLoop): voidRelease the poller.
eventLoopCloseWakeup
pub fn eventLoopCloseWakeup(el: &EventLoop, wakeupId: i32): void // darwin, linux
pub fn eventLoopCloseWakeup(el: &EventLoop, _wakeupId: i32): void // windowsTear down a wakeup made by eventLoopInitWakeup.
eventLoopDrainWakeup
pub fn eventLoopDrainWakeup(_el: &EventLoop, _wakeupId: i32): void // darwin, windows
pub fn eventLoopDrainWakeup(_el: &EventLoop, wakeupId: i32): void // linuxConsume a delivered wakeup so the next poll does not report it again.
eventLoopFd
pub fn eventLoopFd(el: &EventLoop): i32The poller's descriptor, to hand to another runtime's poller when embedding.
eventLoopFromFd
pub fn eventLoopFromFd(fd: i32): EventLoop // darwin, linux
pub fn eventLoopFromFd(slot: i32): EventLoop // windowsWrap a poller that another runtime already owns. Does not take ownership: closing it is still the original owner's job.
eventLoopInitWakeup
pub fn eventLoopInitWakeup(el: &EventLoop): i32Arm a wakeup that breaks a blocked eventPoll from elsewhere; this is how a Promise.blocking worker tells the scheduler its result is ready. Returns the id to pass to eventLoopNotify, eventLoopDrainWakeup and eventLoopCloseWakeup.
eventLoopNotify
pub fn eventLoopNotify(el: &EventLoop, wakeupId: i32): i32 // darwin
pub fn eventLoopNotify(_el: &EventLoop, wakeupId: i32): i32 // linux
pub fn eventLoopNotify(el: &EventLoop, _wakeupId: i32): i32 // windowssafe from any thread: kevent on a shared kqueue is thread-safe
eventPoll
pub fn eventPoll(el: &EventLoop, readyFds: *i32, maxEvents: i32, timeoutMs: i32): i32 // darwin, linux
pub fn eventPoll(el: &EventLoop, readyFds: *i32, _maxEvents: i32, timeoutMs: i32): i32 // windowspoll for ready events. readyFds: caller-allocated *i32 array with capacity >= maxEvents. returns count of ready fds, or -1 on error. timeoutMs < 0 means block indefinitely.
A fixed [i32; N] array passes bare: it coerces to *i32.
eventRegisterRead
pub fn eventRegisterRead(el: &EventLoop, fd: i32): i32Watch fd for readability.
eventRegisterWrite
pub fn eventRegisterWrite(el: &EventLoop, fd: i32): i32Watch fd for writability.
fionbio
pub fn fionbio(): i32Only on windows.
FIONBIO = 0x8004667E as a signed 32-bit value.
keventSize
pub fn keventSize(): i64Only on darwin.
maskRead
pub fn maskRead(): i32Only on windows.
maskToNet
pub fn maskToNet(mask: i32): i32Only on windows.
maskWrite
pub fn maskWrite(): i32Only on windows.
setNonblocking
pub fn setNonblocking(fd: i32): i32Make fd non-blocking. 0 on success, -1 on error. Register only non-blocking fds: a poller that reports ready and then hangs in read is the usual symptom otherwise, because readiness says a byte was available, not that the next read will return.
wakeupIdentBase
pub fn wakeupIdentBase(): i32Only on darwin, windows.
ident chosen far above any real fd so eventPoll consumers can tell a wakeup apart from fd readiness in the same readyFds array
winLoopTableCap
pub fn winLoopTableCap(): i32Only on windows.
16 concurrent scheduler loops is far beyond any real program.
wlCap
pub fn wlCap(): i64Only on windows.
wlMaskIdx
pub fn wlMaskIdx(i: i64): i64Only on windows.
wlSize
pub fn wlSize(): i64Only on windows.
wlSockIdx
pub fn wlSockIdx(i: i64): i64Only on windows.
wlWevtIdx
pub fn wlWevtIdx(i: i64): i64Only on windows.