diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go index d2828b138a..92873f2dac 100644 --- a/src/runtime/lock_futex.go +++ b/src/runtime/lock_futex.go @@ -230,7 +230,7 @@ func notetsleepg(n *note, ns int64) bool { return ok } -func beforeIdle() bool { +func beforeIdle(int64) bool { return false } diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go index 23f17080f2..51cbe60607 100644 --- a/src/runtime/lock_js.go +++ b/src/runtime/lock_js.go @@ -111,6 +111,8 @@ func notetsleepg(n *note, ns int64) bool { gopark(nil, nil, waitReasonSleep, traceEvNone, 1) clearTimeoutEvent(id) // note might have woken early, clear timeout + clearIdleID() + mp = acquirem() delete(notes, n) delete(notesWithTimeout, n) @@ -148,10 +150,25 @@ var isHandlingEvent = false var nextEventIsAsync = false var returnedEventHandler *g +// The timeout event started by beforeIdle. +var idleID int32 + // beforeIdle gets called by the scheduler if no goroutine is awake. // If we are not already handling an event, then we pause for an async event. // If an event handler returned, we resume it and it will pause the execution. -func beforeIdle() bool { +func beforeIdle(delay int64) bool { + if delay > 0 { + if delay < 1e6 { + delay = 1 + } else if delay < 1e15 { + delay = delay / 1e6 + } else { + // An arbitrary cap on how long to wait for a timer. + // 1e9 ms == ~11.5 days. + delay = 1e9 + } + idleID = scheduleTimeoutEvent(delay) + } if !isHandlingEvent { nextEventIsAsync = true pause(getcallersp() - 16) @@ -164,6 +181,14 @@ func beforeIdle() bool { return false } +// clearIdleID clears our record of the timeout started by beforeIdle. +func clearIdleID() { + if idleID != 0 { + clearTimeoutEvent(idleID) + idleID = 0 + } +} + // pause sets SP to newsp and pauses the execution of Go's WebAssembly code until an event is triggered. func pause(newsp uintptr) @@ -189,6 +214,8 @@ func handleEvent() { eventHandler() + clearIdleID() + // wait until all goroutines are idle returnedEventHandler = getg() gopark(nil, nil, waitReasonZero, traceEvNone, 1) diff --git a/src/runtime/lock_sema.go b/src/runtime/lock_sema.go index 9507d46f41..af9517d744 100644 --- a/src/runtime/lock_sema.go +++ b/src/runtime/lock_sema.go @@ -289,7 +289,7 @@ func notetsleepg(n *note, ns int64) bool { return ok } -func beforeIdle() bool { +func beforeIdle(int64) bool { return false } diff --git a/src/runtime/proc.go b/src/runtime/proc.go index 71e756b991..fd93a3db5f 100644 --- a/src/runtime/proc.go +++ b/src/runtime/proc.go @@ -2355,7 +2355,7 @@ stop: // wasm only: // If a callback returned and no other goroutine is awake, // then pause execution until a callback was triggered. - if beforeIdle() { + if beforeIdle(delta) { // At least one goroutine got woken. goto top }