Skip to content

Commit e374a4f

Browse files
runtime: keep scheduler_cooperative idle-wait calls direct
TestBinarySize/hifive1b/examples/echo regressed by 32 bytes after the previous commit routed the scheduler's idle wait through a schedulerIdleWait helper. The extra call frame + branch landed on every non-wasip1 cooperative target, where the original direct sleepTicks / waitForEvents calls compile to a single inlined call. Push the wasip1-specific FD-polling logic down into the wasip1 sleepTicks / waitForEvents definitions themselves and revert scheduler_cooperative.go to its pre-PR shape. Non-wasip1 targets now produce the exact same code they did before this PR. On wasip1, sleepTicks routes through pollIO when FD waiters are registered (unchanged behavior, just relocated). - scheduler_cooperative.go: back to sleepTicks / waitForEvents. - scheduler_idle_wasip1.go: defines the integrated sleepTicks + waitForEvents for wasip1 cooperative builds. - scheduler_idle_wasip1_none.go: fallback definitions for wasip1 builds that don't use the cooperative scheduler (-scheduler=none/threads). - runtime_wasip1.go: drop the now-relocated sleepTicks. - wait_other.go: exclude wasip1 (covered by the two files above). - scheduler_idle_other.go: removed; no longer needed.
1 parent ceb3a6f commit e374a4f

6 files changed

Lines changed: 44 additions & 47 deletions

File tree

src/runtime/runtime_wasip1.go

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,6 @@ var (
7878
sleepTicksNEvents uint32
7979
)
8080

81-
func sleepTicks(d timeUnit) {
82-
sleepTicksSubscription.u.u.timeout = uint64(d)
83-
poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
84-
}
85-
8681
func ticks() timeUnit {
8782
var nano uint64
8883
clock_time_get(0, timePrecisionNanoseconds, &nano)

src/runtime/scheduler_cooperative.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func scheduler(returnAtDeadlock bool) {
178178
// JavaScript is treated specially, see below.
179179
return
180180
}
181-
schedulerIdleWait(0)
181+
waitForEvents()
182182
continue
183183
}
184184

@@ -203,7 +203,7 @@ func scheduler(returnAtDeadlock bool) {
203203
}
204204
}
205205
if timeLeft > 0 {
206-
schedulerIdleWait(timeLeft)
206+
sleepTicks(timeLeft)
207207
if asyncScheduler {
208208
// The sleepTicks function above only sets a timeout at
209209
// which point the scheduler will be called again. It does

src/runtime/scheduler_idle_other.go

Lines changed: 0 additions & 19 deletions
This file was deleted.

src/runtime/scheduler_idle_wasip1.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,31 @@
22

33
package runtime
44

5-
// schedulerIdleWait is called by the cooperative scheduler when the run queue
6-
// is empty. timeoutTicks is the number of monotonic ticks until the next
7-
// sleep / timer is due, or 0 to indicate that no timer is pending and we
8-
// should wait until something external happens (an FD becoming ready).
5+
// sleepTicks is the cooperative scheduler's "wait until the next deadline"
6+
// primitive on wasip1. It is only called by the scheduler when the run queue
7+
// is empty and there's a sleeping task or pending timer due in d ticks.
98
//
10-
// On wasip1 this routes through pollIO so that any FD waiters registered
11-
// via netpollAddWait participate in the same poll_oneoff call as the
12-
// timer. When there are no FD waiters the cheap legacy paths
13-
// (sleepTicks / waitForEvents) are used.
14-
func schedulerIdleWait(timeoutTicks timeUnit) {
15-
if pollCount == 0 {
16-
if timeoutTicks > 0 {
17-
sleepTicks(timeoutTicks)
18-
return
19-
}
20-
// No timer and no FDs to poll on. Genuine deadlock.
21-
waitForEvents()
9+
// If any FD waiters are registered via netpollAddWait, this routes through
10+
// pollIO so the same poll_oneoff call observes both the clock subscription
11+
// and the FD subscriptions. With no FD waiters it falls back to the cheap
12+
// single-clock-subscription path.
13+
func sleepTicks(d timeUnit) {
14+
if pollCount > 0 {
15+
pollIO(ticksToNanoseconds(d))
2216
return
2317
}
24-
if timeoutTicks > 0 {
25-
pollIO(ticksToNanoseconds(timeoutTicks))
18+
sleepTicksSubscription.u.u.timeout = uint64(d)
19+
poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
20+
}
21+
22+
// waitForEvents is the cooperative scheduler's "wait until something external
23+
// happens" primitive. It is only called when both the run queue and the
24+
// timer/sleep queues are empty. With no FD waiters this is a genuine
25+
// deadlock; with FD waiters we block until any of them is ready.
26+
func waitForEvents() {
27+
if pollCount > 0 {
28+
pollIO(-1)
2629
return
2730
}
28-
// FDs registered but no timer — block until any FD is ready.
29-
pollIO(-1)
31+
runtimePanic("deadlocked: no event source")
3032
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//go:build wasip1 && !scheduler.tasks && !scheduler.asyncify
2+
3+
package runtime
4+
5+
// sleepTicks blocks the current execution context for d ticks. This is the
6+
// fallback used when no cooperative scheduler is configured (-scheduler=none
7+
// or -scheduler=threads on wasip1) and it has no FD-polling integration —
8+
// see scheduler_idle_wasip1.go for the cooperative variant.
9+
func sleepTicks(d timeUnit) {
10+
sleepTicksSubscription.u.u.timeout = uint64(d)
11+
poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
12+
}
13+
14+
// waitForEvents is only meaningful when there's an event source available.
15+
// Without the cooperative scheduler running poll_oneoff on FDs, wasip1 has
16+
// nothing to wake on, so this is a hard deadlock.
17+
func waitForEvents() {
18+
runtimePanic("deadlocked: no event source")
19+
}

src/runtime/wait_other.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//go:build !tinygo.riscv && !cortexm && !(linux && !baremetal && !tinygo.wasm && !nintendoswitch) && !darwin
1+
//go:build !tinygo.riscv && !cortexm && !(linux && !baremetal && !tinygo.wasm && !nintendoswitch) && !darwin && !wasip1
22

33
package runtime
44

0 commit comments

Comments
 (0)