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 47de8027b9
commit b5ddee3e4a
2 changed files with 28 additions and 8 deletions

View File

@ -237,7 +237,8 @@ func (i *Input) updateFromEvent(e js.Value) error {
i.updateTouchesFromEvent(e) i.updateTouchesFromEvent(e)
} }
return i.ui.forceUpdateOnMinimumFPSMode() i.ui.forceUpdateOnMinimumFPSMode()
return nil
} }
func (i *Input) setMouseCursorFromEvent(e js.Value) { func (i *Input) setMouseCursorFromEvent(e js.Value) {

View File

@ -15,6 +15,7 @@
package ui package ui
import ( import (
"sync"
"syscall/js" "syscall/js"
"time" "time"
@ -86,6 +87,8 @@ type userInterfaceImpl struct {
context *context context *context
input Input input Input
m sync.Mutex
} }
func init() { func init() {
@ -275,6 +278,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
@ -488,10 +495,15 @@ 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()
// updateImpl can block. Use goroutine.
// See https://pkg.go.dev/syscall/js#FuncOf.
go func() {
if err := theUI.updateImpl(true); err != nil && theUI.err != nil { if err := theUI.updateImpl(true); err != nil && theUI.err != nil {
theUI.err = err theUI.err = err
return nil return
} }
}()
return nil return nil
})) }))
} }
@ -609,11 +621,18 @@ func setCanvasEventHandlers(v js.Value) {
})) }))
} }
func (u *userInterfaceImpl) forceUpdateOnMinimumFPSMode() error { func (u *userInterfaceImpl) forceUpdateOnMinimumFPSMode() {
if u.fpsMode != FPSModeVsyncOffMinimum { if u.fpsMode != FPSModeVsyncOffMinimum {
return nil 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 && u.err != nil {
u.err = err
}
}()
} }
func (u *userInterfaceImpl) Run(game Game) error { func (u *userInterfaceImpl) Run(game Game) error {