internal/ui: bug fix: reentering updateImpl caused double unlocking

updateImpl can be invoked in multiple ways. This should have been
protected by a mutex, or this caused unexpected reentrance.

Closes #2339
This commit is contained in:
Hajime Hoshi 2022-09-20 13:19:40 +09:00
parent e808f168a1
commit ef26267703

View File

@ -15,6 +15,7 @@
package ui package ui
import ( import (
"sync"
"syscall/js" "syscall/js"
"time" "time"
@ -84,6 +85,8 @@ type userInterfaceImpl struct {
context *context context *context
input Input input Input
m sync.Mutex
} }
func init() { func init() {
@ -273,6 +276,10 @@ func (u *userInterfaceImpl) update() error {
} }
func (u *userInterfaceImpl) updateImpl(force bool) error { func (u *userInterfaceImpl) updateImpl(force bool) error {
// Guard updateImpl as this function cannot be invoked until this finishes (#2339).
u.m.Lock()
defer u.m.Unlock()
// context can be nil when an event is fired but the loop doesn't start yet (#1928). // context can be nil when an event is fired but the loop doesn't start yet (#1928).
if u.context == nil { if u.context == nil {
return nil return nil
@ -482,9 +489,14 @@ func init() {
func setWindowEventHandlers(v js.Value) { func setWindowEventHandlers(v js.Value) {
v.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} { v.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.updateScreenSize() theUI.updateScreenSize()
if err := theUI.updateImpl(true); err != nil {
panic(err) // updateImpl can block. Use goroutine.
} // See https://pkg.go.dev/syscall/js#FuncOf.
go func() {
if err := theUI.updateImpl(true); err != nil {
panic(err)
}
}()
return nil return nil
})) }))
} }
@ -579,7 +591,14 @@ func (u *userInterfaceImpl) forceUpdateOnMinimumFPSMode() {
if u.fpsMode != FPSModeVsyncOffMinimum { if u.fpsMode != FPSModeVsyncOffMinimum {
return return
} }
u.updateImpl(true)
// updateImpl can block. Use goroutine.
// See https://pkg.go.dev/syscall/js#FuncOf.
go func() {
if err := u.updateImpl(true); err != nil {
panic(err)
}
}()
} }
func (u *userInterfaceImpl) Run(game Game) error { func (u *userInterfaceImpl) Run(game Game) error {