add thread affinity: pin a task to a core

spawnOn(entry, priority, cpu) routes to a per-core pinned queue, merged with the global queue at O(1) selection. Falls back to unpinned for an offline/invalid core.
This commit is contained in:
Daniel Samson
2026-07-08 14:45:28 +01:00
parent f02259cae0
commit 37f72a4df8
2 changed files with 118 additions and 43 deletions
+21
View File
@@ -67,6 +67,27 @@ exist, which is what a real-time scheduler needs.
- **Round-robin within a level.** When a task is descheduled it goes to the *back*
of its level's queue, so equal-priority tasks share the CPU fairly.
## Affinity: pinning a task to a core
By default a task runs on **any** core — the ready queue above is global, and any
idle core pulls the highest-priority task from it (work-conserving; see
[smp.md](smp.md)). A task can instead be **pinned** to one core with
`spawnOn(entry, priority, cpu)`, giving it an *affinity*: it will only ever run
there, never migrating.
Mechanically, each core has its **own** pinned queue (same 8-level FIFO + bitmap)
alongside the global one. A pinned task is enqueued only into its core's pinned
queue; selection compares the top of the global queue and the running core's pinned
queue and takes the higher priority (still O(1) — two bit-scans and a compare), with
a pinned task winning an equal-priority tie so it can't be starved by global work.
Because every queue is mutated under the [big kernel lock](smp.md), one core enqueuing
into another core's pinned queue is safe.
This is the *explicit-affinity* model (no surprise migration mid-deadline), which is
the more real-time-predictable direction. `spawnOn` refuses to pin to an offline or
out-of-range core — it creates the task unpinned instead, so it still runs somewhere
rather than stranding in a queue no core services, and returns whether the pin took.
## Sleeping and the idle task
A task can **block** — give up the CPU until an event, rather than busy-wait