Summary
The current scheduler is cooperative by convention. Long-running Go code must call runtime.Gosched() manually, and timer progress depends on returning to the scheduler loop.
Why
- A CPU-bound goroutine can starve other goroutines and delay timer wakeups indefinitely.
- Fairness and frame pacing become application-discipline problems instead of runtime guarantees.
- This is a practical usability issue even if the runtime stays M:1.
Evidence
runtime/runtime_stubs.c: runtime.Gosched() is documented as something callers must do in long-running loops.
runtime/scheduler.c: goroutines run until they yield, block, or exit.
runtime/scheduler.c: check_timers() is called from scheduler_run_loop() only after control returns to the scheduler.
Direction
- Keep the single-threaded scheduler, but add enforced safepoints or deferred preemption at safe runtime boundaries.
- Make timer servicing part of a fairness model, not just the outer scheduler loop.
- Add a budgeted scheduling mode that can cooperate with frame timing and vblank-driven runtimes.
Summary
The current scheduler is cooperative by convention. Long-running Go code must call
runtime.Gosched()manually, and timer progress depends on returning to the scheduler loop.Why
Evidence
runtime/runtime_stubs.c:runtime.Gosched()is documented as something callers must do in long-running loops.runtime/scheduler.c: goroutines run until they yield, block, or exit.runtime/scheduler.c:check_timers()is called fromscheduler_run_loop()only after control returns to the scheduler.Direction