Skip to content

Assign a fibre to a hub - #9

Open
johng wants to merge 3 commits into
robertsdotpm:mainfrom
johng:fibre-hubs
Open

johng wants to merge 3 commits into
robertsdotpm:mainfrom
johng:fibre-hubs

Conversation

@johng

@johng johng commented Sep 13, 2026 •

Copy link
Copy Markdown
Contributor

Adds a small placement API so a test can assert which hub a fibre runs on instead of measuring it statistically.

  • mn_fiber(fn, hub=N) spawns on hub N and keeps it on that hub's local queue rather than the stealable deque.
  • G.pin(N) confines a fibre's later resumes to hub N, enforced on the consumer side of the global run-queue. pin(None) clears it.
  • mn_current_hub() returns the hub running the caller.
  • G.stack() reports state from the wake state machine under migration mode, where there is no snapshot.

Offload spawns are pinned through the same path, so a woken offload fibre cannot be pulled onto a general hub under migration; the world-yield scan now skips offload hubs for the same reason.

Tests: tests/test_hub_pinning.py (needs both CPython migration patches; skips otherwise) and two additions to tests/test_offload_hubs.py.

🤖 Generated with Claude Code

Adds a new assingment API to force scheduling
of a fibre off the global queue. This is useful
for validating hub migation in a determinisic
fashion.
A wake performed on a general hub thread now pushes the woken fiber onto
the waker's own Chase-Lev deque (a lock-free owner push) instead of the
mutex-protected global run-queue.  The waker's hub picks it up at its next
pick step, or an idle hub steals it.  The global queue is left for the
cases with nowhere local to go: a foreign (non-hub) waker, an offload-hub
waker, a pinned fiber, controlled replay, or a full deque.

runloom_mn_woken_enqueue is the single routing point; all three former
global_runq_push sites (wake_g, the RUNNING_WOKEN release, the sweep
release) go through it.  The wake state machine is unchanged: the caller
still takes the PARKED->QUEUED edge and the queue ref, and hub_main now
recognises a QUEUED g popped or stolen from a deque and runs the same
QUEUED->RUNNING claim + queue-ref drop it runs for a global pull.  The
park-side surplus scan counts a deque of ONE as surplus under migration,
because the owner may be mid-fiber and never reach its pick step; the
producer kicks after every local push so the Dekker pair stays exact.
Default mode is byte-unchanged (nothing reaches QUEUED there).

Measured on the 3.14.4t-mig interpreter (both patches), 18-core arm64,
RUNLOOM_MIGRATION=1, cover builds, median of 3:

  8 hubs, 8 concurrent ping-pong pairs   base 341K  ->  new 665K agg rounds/s
      (every wake off the global queue: 992K global pulls -> 0)
  2 hubs, 1 ping-pong pair               base 808K  ->  new 818K rounds/s
  2 hubs, 64-way fan-out                 base 1.30M ->  new 1.36M wakes/s

The single-pair case is within noise: with one waker at a time the global
mutex is uncontended, so the win is the lock under concurrent wakers.

Testing: tests/test_local_wake.py (4 tests; soaked 20x) plus test_mn,
test_chan, test_hub_pinning, test_offload_hubs, test_freethread_stress and
test_swarm_mn_sched (minus the deadlock tests) in migration mode, identical
pass/fail sets on this tree and the unmodified baseline; the default-mode
subset via run_isolated is all green on stock 3.14.4t.

Two pre-existing migration-mode gaps surfaced and are recorded in
docs/dev/MIGRATION_DEFAULT_ANALYSIS.md, not fixed here: the deadlock census
is inert (per-g-tstate disables preemption, which disables the sysmon
instrumentation the census is gated on), and the fiber_n bulk-spawn +
WaitGroup swarm tests time out.  Pinned cross-hub wakes also run at ~3.4K
rounds/s in both trees, which points at the deep-park kick path.
sched: Go-style local wake under migration mode
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant