Closed mihaiav closed 4 years ago
CC @neelance
I managed to debug the issue. What is happening is that https://github.com/golang/go/blob/b0a87544754a41312aa454f69d4e820979f19ef0/src/runtime/lock_js.go#L196-L200 wakes up the innermost event handler, but then findrunnable
of proc.go schedules a different goroutine instead of the e.gp
here. This messes up the wasm event handling. It is a bit strange that another goroutine gets scheduled because the code above (beforeIdle
) got called because no goroutine was awake any more. I guess there is some internal timer logic that if triggered at the very right moment makes some goroutine available.
I see three possible solutions:
gopark(nil, nil, waitReasonZero, traceEvNone, 1)
for events[len(events)-1] != e {
gopark(nil, nil, waitReasonZero, traceEvNone, 1)
}
findrunnable
not check the timers. One way to do this is to change https://github.com/golang/go/blob/b0a87544754a41312aa454f69d4e820979f19ef0/src/runtime/proc.go#L2276-L2279 to
if beforeIdle(delta) {
// At least one goroutine got woken.
if gp, inheritTime := runqget(_p_); gp != nil {
return gp, inheritTime
}
throw("findrunnable: no goroutine woken by beforeIdle")
}
beforeIdle
return the specific goroutine e.gp
that needs to run next. I wasn't able to quickly figure out how to do this.@ianlancetaylor @cherrymui I am not sure which way is a good solution. Do you have any preference?
I like the idea of having beforeIdle
return the goroutine to run. I think findrunnable
could handle it just as it handles the case of netpoll
returning a goroutine: call casgstatus
and traceGoUnpark
, and return it.
Yeah, I like option 3 as well. We may want to try that first.
Thanks for debugging this!
Change https://golang.org/cl/230178 mentions this issue: runtime: fix race condition between timer and event handler
What version of Go are you using (
go version
)?Does this issue reproduce with the latest release?
yes
What operating system and processor architecture are you using (
go env
)?go env
OutputWhat did you do?
I've used github.com/golang/glog for logging throughout a large Single Page Application (go compiled to wasm along with a wrapper for javascript's custom element's API).
What did you expect to see?
Logs being printed in the dev console
What did you see instead?
Once the app became more complex (got many custom elements, more concurrent logging) I started to experience a lot of runtime crashes. The latest/worst behaviour was that the application got into a kind a of deadlock and CPU was being exhausted. I couldn't even profile the CPU because the dev tools were being blocked as well due the CPU drain.
I can't reproduce the CPU drain in a simple test case but the code below (compiled to wasm
GOOS=js GOARCH=wasm go build -o app.wasm
) always throwsfatal error: unreachable
.Below is also a stacktrace from Firefox when my app gets stuck and I kill the tab/script.