FuncID_debugCallV1
FuncID_gopanic
FuncID_panicwrap
- FuncID_handleAsyncEvents
+ FuncID_handleAsyncEvent
FuncID_wrapper // any autogenerated code (hash/eq algorithms, method wrappers, etc.)
)
return FuncID_gopanic
case "runtime.panicwrap":
return FuncID_panicwrap
- case "runtime.handleAsyncEvents":
- return FuncID_handleAsyncEvents
+ case "runtime.handleAsyncEvent":
+ return FuncID_handleAsyncEvent
}
if file == "<autogenerated>" {
return FuncID_wrapper
}
}
+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
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
// 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
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 {