runtime: yield the thread in Gosched on the threads scheduler - #5764
Conversation
c70b116 to
b27da65
Compare
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The implementation is scoped correctly to supported threaded targets and matches the intended scheduler behavior.
Review effort: Balanced
Findings: None
What changed in this PR
Adds OS-thread yielding to runtime.Gosched for the threads scheduler.
Changes:
- Adds
task.Yieldbacked by POSIXsched_yield. - Uses it from the threads scheduler’s
Gosched.
| File | Description |
|---|---|
src/runtime/scheduler_threads.go |
Yields the current OS thread in Gosched. |
src/internal/task/task_threads.go |
Adds the thread-yield wrapper and external declaration. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
OK I looked into a bit more @davecheney The following is edited from an automated review:
I think this is indeed better than what we have now. Count me in favor. |
thanks for the review; i'll take another swing at this tomorrow. |
Gosched was a no-op on the threads scheduler. Add task.Yield, which calls sched_yield, and use it from Gosched to ask the OS scheduler to let other threads run. Each goroutine has its own OS thread. This differs from upstream Go's Gosched, which hands off to the Go scheduler through gosched_m. Upstream uses osyield in spin loops, including locking and stop-the-world code, not to implement Gosched. This reduces an iter test flake on Linux. stableNumGoroutine in iter/pull_test.go uses GOMAXPROCS(1) and Gosched to let the previous subtest's tRunner goroutine finish exiting. TinyGo ignores GOMAXPROCS, so sched_yield cannot guarantee that goroutine has exited or rule out the race. The original Linux stress test under full CPU load saw TestPull and TestPull2 fail 7 of 300 runs before this change and 0 of 300 after. Remove the outdated signal_waitUntilIdle TODO. Its polling loop now yields on each pass with the threads scheduler.
b27da65 to
cd6f742
Compare
|
@deadprogram PTAL |
deadprogram
left a comment
There was a problem hiding this comment.
It is the best option we have @davecheney thanks for working on it.
Gosched was a no-op on the threads scheduler. Add
task.Yield, which callssched_yield, and use it fromGoschedto yield the current thread to the OS scheduler. Each goroutine has its own OS thread. The call lives ininternal/tasknext to the other thread hooks, the same way the cooperative and cores schedulers calltask.Pause.This differs from upstream Go's
Gosched, which hands off to the Go scheduler throughgosched_m. Upstream usesosyieldin spin loops, including locking and stop-the-world code, not to implementGosched.This reduces an iter test flake on Linux.
stableNumGoroutineiniter/pull_test.gousesGOMAXPROCS(1)andGoschedto let the previous subtest'stRunnergoroutine finish exiting. TinyGo ignoresGOMAXPROCS, and with a no-opGoschedthat goroutine could still be counted, making the baseline one too high.sched_yieldis only a scheduling hint. It does not enforceGOMAXPROCS(1)or guarantee that the goroutine has exited, so the race can still occur.The original Linux stress test under full CPU load (8 busy loops on 8 cores) saw
TestPullandTestPull2fail 7 of 300 runs before this change and 0 of 300 after. This is evidence of improvement, not a guarantee that the flake is eliminated.signal_waitUntilIdleinruntime_unix.goalso yields on each polling pass with the threads scheduler. Remove its outdated TODO aboutGoschedleaving it in a busy loop.