runtime,syscall,internal/poll,os: wasip1 poll_oneoff scheduler integration + net.FileListener (#5386)

* runtime,syscall,internal/poll,os: wasip1 poll_oneoff scheduler integration + net.FileListener

On wasip1 today every syscall.Read/Write blocks the entire wasm module — the
cooperative scheduler invokes poll_oneoff only for sleep/timer wakeups, and
there's no path from the net package to a working TCP server. This change
fixes both: it threads poll_oneoff through the scheduler's idle path so a
goroutine doing FD I/O parks instead of blocking the module, and it provides
enough internal/poll / os / syscall surface that upstream Go's
net.FileListener / net.FileConn works on a host-pre-opened TCP socket.

* 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.
This commit is contained in:
Achille
2026-05-26 01:12:55 -07:00
committed by GitHub
parent 85d223c5e2
commit d01a932201
17 changed files with 1404 additions and 51 deletions
+32
View File
@@ -0,0 +1,32 @@
//go:build wasip1 && (scheduler.tasks || scheduler.asyncify)
package runtime
// sleepTicks is the cooperative scheduler's "wait until the next deadline"
// primitive on wasip1. It is only called by the scheduler when the run queue
// is empty and there's a sleeping task or pending timer due in d ticks.
//
// If any FD waiters are registered via netpollAddWait, this routes through
// pollIO so the same poll_oneoff call observes both the clock subscription
// and the FD subscriptions. With no FD waiters it falls back to the cheap
// single-clock-subscription path.
func sleepTicks(d timeUnit) {
if pollCount > 0 {
pollIO(ticksToNanoseconds(d))
return
}
sleepTicksSubscription.u.u.timeout = uint64(d)
poll_oneoff(&sleepTicksSubscription, &sleepTicksResult, 1, &sleepTicksNEvents)
}
// waitForEvents is the cooperative scheduler's "wait until something external
// happens" primitive. It is only called when both the run queue and the
// timer/sleep queues are empty. With no FD waiters this is a genuine
// deadlock; with FD waiters we block until any of them is ready.
func waitForEvents() {
if pollCount > 0 {
pollIO(-1)
return
}
runtimePanic("deadlocked: no event source")
}