From 2686e7494845dae877e0efb4ff786c672b2cd2ef Mon Sep 17 00:00:00 2001 From: Richard Musiol Date: Thu, 10 Oct 2019 22:38:26 +0200 Subject: [PATCH] runtime: make goroutine for wasm async events short-lived An extra goroutine is necessary to handle asynchronous events on wasm. However, we do not want this goroutine to exist all the time. This change makes it short-lived, so it ends after the asynchronous event was handled. Fixes #34768 Change-Id: I24626ff0af9d803a01ebe33fbb584d04d2059a44 Reviewed-on: https://go-review.googlesource.com/c/go/+/200497 Run-TryBot: Richard Musiol TryBot-Result: Gobot Gobot Reviewed-by: Cherry Zhang --- src/cmd/internal/objabi/funcid.go | 6 ++--- src/runtime/lock_js.go | 45 ++++++++++++++++++------------- src/runtime/symtab.go | 2 +- src/runtime/traceback.go | 4 +-- 4 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/cmd/internal/objabi/funcid.go b/src/cmd/internal/objabi/funcid.go index fe54eba663..c13c3cb458 100644 --- a/src/cmd/internal/objabi/funcid.go +++ b/src/cmd/internal/objabi/funcid.go @@ -37,7 +37,7 @@ const ( FuncID_debugCallV1 FuncID_gopanic FuncID_panicwrap - FuncID_handleAsyncEvents + FuncID_handleAsyncEvent FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) ) @@ -83,8 +83,8 @@ func GetFuncID(name, file string) FuncID { return FuncID_gopanic case "runtime.panicwrap": return FuncID_panicwrap - case "runtime.handleAsyncEvents": - return FuncID_handleAsyncEvents + case "runtime.handleAsyncEvent": + return FuncID_handleAsyncEvent } if file == "" { return FuncID_wrapper diff --git a/src/runtime/lock_js.go b/src/runtime/lock_js.go index d08238ce3c..23f17080f2 100644 --- a/src/runtime/lock_js.go +++ b/src/runtime/lock_js.go @@ -144,28 +144,19 @@ func checkTimeouts() { } } +var isHandlingEvent = false +var nextEventIsAsync = false var returnedEventHandler *g -func init() { - // At the toplevel we need an extra goroutine that handles asynchronous events. - initg := getg() - go handleAsyncEvents(initg) - gopark(nil, nil, waitReasonZero, traceEvNone, 1) -} - -func handleAsyncEvents(initg *g) { - returnedEventHandler = getg() - goready(initg, 1) - - gopark(nil, nil, waitReasonZero, traceEvNone, 1) - returnedEventHandler = nil - - pause(getcallersp() - 16) -} - // beforeIdle gets called by the scheduler if no goroutine is awake. -// We resume the event handler (if available) which will pause the execution. +// 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 { + if !isHandlingEvent { + nextEventIsAsync = true + pause(getcallersp() - 16) + return true + } if returnedEventHandler != nil { goready(returnedEventHandler, 1) return true @@ -184,20 +175,36 @@ func scheduleTimeoutEvent(ms int64) int32 func clearTimeoutEvent(id int32) func handleEvent() { + if nextEventIsAsync { + nextEventIsAsync = false + checkTimeouts() + go handleAsyncEvent() + return + } + + prevIsHandlingEvent := isHandlingEvent + isHandlingEvent = true prevReturnedEventHandler := returnedEventHandler returnedEventHandler = nil - checkTimeouts() eventHandler() + // wait until all goroutines are idle returnedEventHandler = getg() gopark(nil, nil, waitReasonZero, traceEvNone, 1) + isHandlingEvent = prevIsHandlingEvent returnedEventHandler = prevReturnedEventHandler pause(getcallersp() - 16) } +func handleAsyncEvent() { + isHandlingEvent = true + eventHandler() + isHandlingEvent = false +} + var eventHandler func() //go:linkname setEventHandler syscall/js.setEventHandler diff --git a/src/runtime/symtab.go b/src/runtime/symtab.go index 8296a8590d..df6e02f62a 100644 --- a/src/runtime/symtab.go +++ b/src/runtime/symtab.go @@ -253,7 +253,7 @@ const ( funcID_debugCallV1 funcID_gopanic funcID_panicwrap - funcID_handleAsyncEvents + funcID_handleAsyncEvent funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) ) diff --git a/src/runtime/traceback.go b/src/runtime/traceback.go index 96fb33c04b..0e4b75a7e6 100644 --- a/src/runtime/traceback.go +++ b/src/runtime/traceback.go @@ -998,7 +998,7 @@ func topofstack(f funcInfo, g0 bool) bool { // isSystemGoroutine reports whether the goroutine g must be omitted // in stack dumps and deadlock detector. This is any goroutine that // starts at a runtime.* entry point, except for runtime.main, -// runtime.handleAsyncEvents (wasm only) and sometimes runtime.runfinq. +// runtime.handleAsyncEvent (wasm only) and sometimes runtime.runfinq. // // If fixed is true, any goroutine that can vary between user and // system (that is, the finalizer goroutine) is considered a user @@ -1009,7 +1009,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool { if !f.valid() { return false } - if f.funcID == funcID_runtime_main || f.funcID == funcID_handleAsyncEvents { + if f.funcID == funcID_runtime_main || f.funcID == funcID_handleAsyncEvent { return false } if f.funcID == funcID_runfinq { -- 2.50.0