Expand description
RingExecutor: an async executor whose READY QUEUE is built from
SubEtha rings. The future’s handle rides through a ring; the ring IS
the scheduler, not a data channel beside one.
This is the deeper async shape than crate::waker_ring. There the
ring carries message bytes and the future lives in a heap queue
(Mutex<VecDeque> in crate::task_pool). Here the future’s
Arc<Task> handle is the ring payload: a worker pops a handle,
reconstructs the Arc, polls it; a wake() pushes the handle back
into a ring. Scheduling = a ring push; running = a ring pop.
§Shape-adaptive ready queue
The ready queue is NOT one ring. A single Vyukov ring funnels every
core’s CAS through one counter and walls throughput as cores climb
(Vyukov contention rises sharply past a handful of producers). So
the executor shards the ready queue to the hardware: ONE ready-ring
shard per worker, worker count taken from
std::thread::available_parallelism (or supplied explicitly).
Each worker owns a home shard, drains it first, and STEALS from the
other shards round-robin when its own is empty. A task is homed to
one shard round-robin at spawn and always reschedules there, so a
self-waking task’s handle stays on one ring (locality) and the home
worker is almost always the only thread touching it. The ring count
equals the core count: 1 core -> 1 ring, a 44-thread host -> 44
rings, no single-counter wall.
Workers pin to distinct cores best-effort
(crate::cpu_affinity); on a host without an affinity API they
run unpinned.
§Why this answers “uncapped consumers”
WORKERS are the hardware parallelism - a small, fixed cap matched to
the machine. TASKS are unbounded: they are Arc<Task> handles
multiplexed onto the worker pool through the rings, not threads. A
44-thread host drives an arbitrary task population on 44 workers.
§Handle / refcount discipline
A task is in its home ring at most once, gated by a scheduled
flag:
spawn/wakeflipscheduledfalse->true and push oneArc::into_rawhandle. A redundant wake (flag already true) drops its clone instead of double-pushing.- a worker pops a handle,
Arc::from_rawreclaims that ref, clearsscheduled, and polls.Readydrops the future and the run-ref;Pendingdrops the run-ref, leaving the future’s stashed waker clone as the liveness anchor until the next wake re-pushes.
Because each live task occupies at most one slot of its home shard,
a shard sized to >= peak tasks homed there never returns Full;
the round-robin home spread keeps that at about peak_tasks / shards. A Full push (only possible under an adversarial
liveness/home correlation) spins until a stealer drains the shard,
which is deadlock-free whenever more than one worker runs.
Structs§
- Ring
Executor - A hardware-shaped pool of worker threads draining an unbounded set of tasks through per-worker SubEtha ready-ring shards.