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)
}
return i.ui.forceUpdateOnMinimumFPSMode()
i.ui.forceUpdateOnMinimumFPSMode()
return nil
}
func (i *Input) setMouseCursorFromEvent(e js.Value) {

View File

@ -15,6 +15,7 @@
package ui
import (
"sync"
"syscall/js"
"time"
@ -86,6 +87,8 @@ type userInterfaceImpl struct {
context *context
input Input
m sync.Mutex
}
func init() {
@ -275,6 +278,10 @@ func (u *userInterfaceImpl) update() 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).
if u.context == nil {
return nil
@ -488,10 +495,15 @@ func init() {
func setWindowEventHandlers(v js.Value) {
v.Call("addEventListener", "resize", js.FuncOf(func(this js.Value, args []js.Value) interface{} {
theUI.updateScreenSize()
if err := theUI.updateImpl(true); err != nil && theUI.err != nil {
theUI.err = err
return nil
}
// 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 {
theUI.err = err
return
}
}()
return nil
}))
}
@ -609,11 +621,18 @@ func setCanvasEventHandlers(v js.Value) {
}))
}
func (u *userInterfaceImpl) forceUpdateOnMinimumFPSMode() error {
func (u *userInterfaceImpl) forceUpdateOnMinimumFPSMode() {
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 {