Commit 2686e749 authored by Richard Musiol's avatar Richard Musiol Committed by Richard Musiol

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 <neelance@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: default avatarCherry Zhang <cherryyz@google.com>
parent 426bfbe9
...@@ -37,7 +37,7 @@ const ( ...@@ -37,7 +37,7 @@ const (
FuncID_debugCallV1 FuncID_debugCallV1
FuncID_gopanic FuncID_gopanic
FuncID_panicwrap FuncID_panicwrap
FuncID_handleAsyncEvents FuncID_handleAsyncEvent
FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
) )
...@@ -83,8 +83,8 @@ func GetFuncID(name, file string) FuncID { ...@@ -83,8 +83,8 @@ func GetFuncID(name, file string) FuncID {
return FuncID_gopanic return FuncID_gopanic
case "runtime.panicwrap": case "runtime.panicwrap":
return FuncID_panicwrap return FuncID_panicwrap
case "runtime.handleAsyncEvents": case "runtime.handleAsyncEvent":
return FuncID_handleAsyncEvents return FuncID_handleAsyncEvent
} }
if file == "<autogenerated>" { if file == "<autogenerated>" {
return FuncID_wrapper return FuncID_wrapper
......
...@@ -144,28 +144,19 @@ func checkTimeouts() { ...@@ -144,28 +144,19 @@ func checkTimeouts() {
} }
} }
var isHandlingEvent = false
var nextEventIsAsync = false
var returnedEventHandler *g 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. // 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 { func beforeIdle() bool {
if !isHandlingEvent {
nextEventIsAsync = true
pause(getcallersp() - 16)
return true
}
if returnedEventHandler != nil { if returnedEventHandler != nil {
goready(returnedEventHandler, 1) goready(returnedEventHandler, 1)
return true return true
...@@ -184,20 +175,36 @@ func scheduleTimeoutEvent(ms int64) int32 ...@@ -184,20 +175,36 @@ func scheduleTimeoutEvent(ms int64) int32
func clearTimeoutEvent(id int32) func clearTimeoutEvent(id int32)
func handleEvent() { func handleEvent() {
if nextEventIsAsync {
nextEventIsAsync = false
checkTimeouts()
go handleAsyncEvent()
return
}
prevIsHandlingEvent := isHandlingEvent
isHandlingEvent = true
prevReturnedEventHandler := returnedEventHandler prevReturnedEventHandler := returnedEventHandler
returnedEventHandler = nil returnedEventHandler = nil
checkTimeouts()
eventHandler() eventHandler()
// wait until all goroutines are idle
returnedEventHandler = getg() returnedEventHandler = getg()
gopark(nil, nil, waitReasonZero, traceEvNone, 1) gopark(nil, nil, waitReasonZero, traceEvNone, 1)
isHandlingEvent = prevIsHandlingEvent
returnedEventHandler = prevReturnedEventHandler returnedEventHandler = prevReturnedEventHandler
pause(getcallersp() - 16) pause(getcallersp() - 16)
} }
func handleAsyncEvent() {
isHandlingEvent = true
eventHandler()
isHandlingEvent = false
}
var eventHandler func() var eventHandler func()
//go:linkname setEventHandler syscall/js.setEventHandler //go:linkname setEventHandler syscall/js.setEventHandler
......
...@@ -253,7 +253,7 @@ const ( ...@@ -253,7 +253,7 @@ const (
funcID_debugCallV1 funcID_debugCallV1
funcID_gopanic funcID_gopanic
funcID_panicwrap funcID_panicwrap
funcID_handleAsyncEvents funcID_handleAsyncEvent
funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.) funcID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
) )
......
...@@ -998,7 +998,7 @@ func topofstack(f funcInfo, g0 bool) bool { ...@@ -998,7 +998,7 @@ func topofstack(f funcInfo, g0 bool) bool {
// isSystemGoroutine reports whether the goroutine g must be omitted // isSystemGoroutine reports whether the goroutine g must be omitted
// in stack dumps and deadlock detector. This is any goroutine that // in stack dumps and deadlock detector. This is any goroutine that
// starts at a runtime.* entry point, except for runtime.main, // 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 // If fixed is true, any goroutine that can vary between user and
// system (that is, the finalizer goroutine) is considered a user // system (that is, the finalizer goroutine) is considered a user
...@@ -1009,7 +1009,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool { ...@@ -1009,7 +1009,7 @@ func isSystemGoroutine(gp *g, fixed bool) bool {
if !f.valid() { if !f.valid() {
return false return false
} }
if f.funcID == funcID_runtime_main || f.funcID == funcID_handleAsyncEvents { if f.funcID == funcID_runtime_main || f.funcID == funcID_handleAsyncEvent {
return false return false
} }
if f.funcID == funcID_runfinq { if f.funcID == funcID_runfinq {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment