backend
nimony/lib/std/ioring/core/backend.nim
const CqSize: int64const MaxOps: int64type BackendRelays = object poll: proc (timeoutMs: int64): bool {.nimcall.} waits: bool close: proc () {.nimcall.} forgetFd: proc (fd: int32) {.nimcall.}
proc ioLanes(): int64Number of independent I/O lanes: one per pool worker, plus one trailing lane shared by every non-worker submitter.
proc ioLane(): int64The lane this thread owns. Every piece of per-thread ring state (the deferred op queue, the slot arena, the backend's poller instance) is indexed by this and by nothing else.
It is deliberately not
threadIdx: that is a threadvar defaulting to 0, so the main thread — which submits and polls throughwaitCompletions— reported the same index as worker 0 and the two drove one unlocked arena and one single-producer io_uring submission ring in parallel.Caveat: all non-worker threads share the trailing lane, so the ring still supports only one foreign submitter at a time (the usual "main thread drives the ring" shape). Ops submitted on that lane are drained by that thread's own
poll, i.e. by itswaitCompletions/pollCompletionsloop.var gOpQueues: seqproc initOpQueues()var gSlots: seqproc initSlots()type TimerEntry = object at: Deadline slot: int32 gen: uint32
type TimerHeap = object a: seq
proc len(h: TimerHeap): int64proc push(h: var TimerHeap; e: TimerEntry)proc popMin(h: var TimerHeap): TimerEntryvar gTimers: seqproc initTimers()proc armDeadline(lane: int64; slotIdx: int64)Record the deadline of the op just allocated into
slotIdx. Aneverdeadline arms nothing, so the heap holds only ops that can actually expire.proc nextDeadline(lane: int64): DeadlineThe earliest deadline this lane is waiting on, skipping entries whose op has already completed.
neverwhen there is nothing to wait for.proc waitMillis(lane: int64; requested: int64): int64How long the backend may actually block: what the caller asked for, or the time to the earliest deadline, whichever is sooner. This is what turns a fixed poll interval into "sleep exactly until something is due".
proc waitNanos(lane: int64; requestedMs: int64): int64waitMillisin nanoseconds, for a backend whose wait is atimespec.-1is "no bound at all", which only happens when the caller asked for none AND the lane is waiting on nothing that can expire.var gNextSeq: uint32var gCqLock: TicketLockvar gCq: seqvar gCqHead: int64var gCqTail: int64var gCqCount: int64const IoTimedOut: int64Completion result for an op whose deadline passed.
ETIMEDOUTon Linux, and negative like every other failure the ring reports.proc complete(slotIdx: int64; res: int64)var gCancelInFlight: proc (slotIdx: int64, gen: uint32) {.nimcall.}Set by a backend where the OS keeps working on an op after this process has stopped waiting for it. The readiness backends leave it
nil: an epoll/kqueue registration owns nothing, so dropping it is the whole of cancelling. io_uring is different — the kernel holds the op's buffer until it acknowledges a cancel, so an op completed here on a blown deadline must still be taken away from the kernel, or it writes into a buffer whose owner has moved on.proc expireDeadlines(lane: int64)Complete every op in this lane whose deadline has passed. Called by each backend after it waits, so a deadline fires whether or not any I/O did.
This is what makes "nothing parks forever" structural: an op cannot be submitted without a deadline, and every deadline is either met or arrives here. A caller parked on a peer that has gone quiet is resumed with
IoTimedOutrather than being left for the process's lifetime.