From 10e7bc994f47a71472c49f84ab782fdfe44bf22e Mon Sep 17 00:00:00 2001 From: Ian Lance Taylor Date: Wed, 10 Apr 2019 17:18:22 -0700 Subject: [PATCH] runtime: add wasm support for timers on P's When we put timers on P's, the wasm code will not be able to rely on the timer goroutine. Use the beforeIdle hook to schedule a wakeup. Updates #6239 Updates #27707 Change-Id: Idf6309944778b8c3d7178f5d09431940843ea233 Reviewed-on: https://go-review.googlesource.com/c/go/+/171827 Run-TryBot: Ian Lance Taylor TryBot-Result: Gobot Gobot Reviewed-by: Michael Knyszek Reviewed-by: Emmanuel Odeke --- src/runtime/lock_futex.go | 2 +- src/runtime/lock_js.go | 29 ++++++++++++++++++++++++++++- src/runtime/lock_sema.go | 2 +- src/runtime/proc.go | 2 +- 4 files changed, 31 insertions(+), 4 deletions(-) diff --git a/src/runtime/lock_futex.go b/src/runtime/lock_futex.go index d2828b138ab..92873f2daca 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 23f17080f26..51cbe606071 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 9507d46f41c..af9517d7440 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 71e756b991c..fd93a3db5fc 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 }