Skip to content

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.

milo
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.

milo
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 ​

milo
pub struct EventLoop

A kqueue/epoll/IOCP poller handle. Create one with EventLoop.new(); release it with eventLoopClose.

EventLoop.new ​

milo
fn EventLoop.new(): Result<EventLoop, string>

Create a poller. Errs if the kernel will not hand one out.

Functions ​

clearNonblocking ​

milo
pub fn clearNonblocking(fd: i32): i32

Clear 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 ​

milo
pub fn efdCloexec(): i32

Only on linux.

efdNonblock ​

milo
pub fn efdNonblock(): i32

Only on linux.

epollEventSize ​

milo
pub fn epollEventSize(): i64

Only on linux.

eventDeregister ​

milo
pub fn eventDeregister(el: &EventLoop, fd: i32, forWrite: bool): i32 // darwin, windows
pub fn eventDeregister(el: &EventLoop, fd: i32, _forWrite: bool): i32 // linux

Stop watching fd. forWrite picks the write registration rather than the read one.

eventLoopClose ​

milo
pub fn eventLoopClose(el: &EventLoop): void

Release the poller.

eventLoopCloseWakeup ​

milo
pub fn eventLoopCloseWakeup(el: &EventLoop, wakeupId: i32): void // darwin, linux
pub fn eventLoopCloseWakeup(el: &EventLoop, _wakeupId: i32): void // windows

Tear down a wakeup made by eventLoopInitWakeup.

eventLoopDrainWakeup ​

milo
pub fn eventLoopDrainWakeup(_el: &EventLoop, _wakeupId: i32): void // darwin, windows
pub fn eventLoopDrainWakeup(_el: &EventLoop, wakeupId: i32): void // linux

Consume a delivered wakeup so the next poll does not report it again.

eventLoopFd ​

milo
pub fn eventLoopFd(el: &EventLoop): i32

The poller's descriptor, to hand to another runtime's poller when embedding.

eventLoopFromFd ​

milo
pub fn eventLoopFromFd(fd: i32): EventLoop // darwin, linux
pub fn eventLoopFromFd(slot: i32): EventLoop // windows

Wrap a poller that another runtime already owns. Does not take ownership: closing it is still the original owner's job.

eventLoopInitWakeup ​

milo
pub fn eventLoopInitWakeup(el: &EventLoop): i32

Arm 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 ​

milo
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 // windows

safe from any thread: kevent on a shared kqueue is thread-safe

eventPoll ​

milo
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 // windows

poll 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 ​

milo
pub fn eventRegisterRead(el: &EventLoop, fd: i32): i32

Watch fd for readability.

eventRegisterWrite ​

milo
pub fn eventRegisterWrite(el: &EventLoop, fd: i32): i32

Watch fd for writability.

fionbio ​

milo
pub fn fionbio(): i32

Only on windows.

FIONBIO = 0x8004667E as a signed 32-bit value.

keventSize ​

milo
pub fn keventSize(): i64

Only on darwin.

maskRead ​

milo
pub fn maskRead(): i32

Only on windows.

maskToNet ​

milo
pub fn maskToNet(mask: i32): i32

Only on windows.

maskWrite ​

milo
pub fn maskWrite(): i32

Only on windows.

setNonblocking ​

milo
pub fn setNonblocking(fd: i32): i32

Make 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 ​

milo
pub fn wakeupIdentBase(): i32

Only 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 ​

milo
pub fn winLoopTableCap(): i32

Only on windows.

16 concurrent scheduler loops is far beyond any real program.

wlCap ​

milo
pub fn wlCap(): i64

Only on windows.

wlMaskIdx ​

milo
pub fn wlMaskIdx(i: i64): i64

Only on windows.

wlSize ​

milo
pub fn wlSize(): i64

Only on windows.

wlSockIdx ​

milo
pub fn wlSockIdx(i: i64): i64

Only on windows.

wlWevtIdx ​

milo
pub fn wlWevtIdx(i: i64): i64

Only on windows.