poll
nimony/lib/std/ioring/backends/poll.nim
var reArmEvent: proc (fd: int32, events: set[IoEvent], alreadyRegistered: bool): bool {.nimcall.}proc armEventsForFd(fd: int32): set[IoEvent]The union of the directions every op currently pending on
fdwaits for.It has to be the union, never one op's own direction: the registration is keyed by fd, and epoll's
EPOLL_CTL_MODreplaces the interest set. Arming with just the newest op's direction therefore silently disarms the others —submitWrite(fd)followed bysubmitRead(fd)would leave the fd watched for EPOLLIN only and the pending write would never be woken.const ArmFailed: int64Completion result for an op on a fd that could not be armed — the same value a failed read or write reports.
proc failPendingForFd(fd: int32)Complete every op pending on
fd. Nothing will make them ready once the fd cannot be armed, so otherwise they park forever.proc submitForPoll(fd: int32; alreadyRegistered: bool)Arm
fdfor every op pending on it, including the one just allocated by the caller (allocSlothas already linked it into the fd's list).An op with no fd has nothing to arm, and arming anyway is not merely useless — the arena lists ops by fd, so every fd-less op shares the
-1bucket. On epoll,epoll_ctlon-1fails with EBADF, which is read as "this fd will never deliver readiness" and fails every op in the bucket: one nop would complete every pending timer with an error. On kqueue the arm silently does nothing instead, so the same nop hangs forever. Neither is a bug the caller can do anything about, so fd-less ops do not come here at all.proc submitPositioned(s: ref SlotObj not nil)epoll cannot register regular files. Execute the positioned syscall directly when draining the queue, rather than waiting for readiness. This may block the polling lane; a deadline cannot interrupt the call.
proc completeOpen(s: ref SlotObj not nil; path: cstring not nil; mode: FileMode; permissions: set[FilePermission])The backend half of
submitOpen: anopenfor an opOpen is performed here, on the polling thread, like every other command a backend runs — never by the caller, who would block on a filesystem-backed open. The fd (or-errno) is completed to the parked caller.proc completeSocket(s: ref SlotObj not nil; domain: Domain; typ: SockType; proto: Protocol)The backend half of
submitSocket: socket(2) is one syscall with no readiness to wait on, so — exactly likecompleteOpen— the polling thread performs it and completes with the fd (or-errno). The flag just created has nothing to arm: its O_NONBLOCK comes from a ring op, not from waiting on it.proc completeSetSockOpt(s: ref SlotObj not nil; fd: int32; level: int32; optName: int32; optVal: pointer; optLen: uint32)The backend half of
submitSetSockOpt: setsockopt(2) answers immediately (it is configuration, not I/O), so the polling thread makes the call and completes with0or-errno. The option value is read during this very call, long before the caller's frame could go away.proc completeBind(s: ref SlotObj not nil; fd: int32; sa: pointer not nil; saLen: uint32)The backend half of
submitBind: bind(2) is one syscall the kernel answers at once — nothing to wait on, so the polling thread performs it and completes with0or-errno. The kernel copies the address out ofsaduring the call; it is not kept.proc completeSetNonBlocking(s: ref SlotObj not nil; fd: int32)The backend half of
submitSetNonBlocking: fcntl(FSETFL, … or ONONBLOCK), two instant syscalls made here on the polling thread — a poller cannot watch for "not blocking", so this is done at issue, not awaited. Completes with0or-errno.proc startConnect(fd: int32; s: ref SlotObj not nil): boolKick off a non-blocking connect on the op in
s. True when the attempt is under way and the poller should watch for writability.False means it is already over — and then this completes the slot, because nothing else will: an op that is never armed gets no readiness event, so leaving it here would park the caller until its deadline no matter how the connect actually went.
proc processFd(fd: int32; firedEvents: set[IoEvent])Dispatch every pending op on
fdwhose direction actually matches the readiness that just fired.firedEvents(as delivered by the poller) is authoritative: a write-readiness wakeup must not drive a still-pending read op (and vice versa) — the fd may be registered for both directions at once (e.g. a socket with an in-flight read and an in-flight write), and only the direction that actually fired has data ready / a free send buffer.The descriptors must be O_NONBLOCK (see
submitSetNonBlocking): a transfer that would block answersEAGAIN, which leaves the op pending.proc completeCommand(s: ref SlotObj not nil; op: var OpContext): boolThe ops that are one instant call with no readiness to wait on — open, socket, setsockopt, bind, non-blocking — performed right here on the polling thread.
falsefor every other kind, which the caller arms.