From 40a2c0a6c3b2ebbbc5149eecfb6bdc2d40547224 Mon Sep 17 00:00:00 2001 From: Sergey Stepanov Date: Fri, 20 Oct 2023 18:50:04 +0300 Subject: [PATCH] Add start/stop frontend lock --- pkg/worker/caged/libretro/frontend.go | 48 +++++++++++----------- pkg/worker/caged/libretro/frontend_test.go | 17 +++++++- pkg/worker/coordinatorhandlers.go | 2 +- 3 files changed, 40 insertions(+), 27 deletions(-) diff --git a/pkg/worker/caged/libretro/frontend.go b/pkg/worker/caged/libretro/frontend.go index 052756a69..80392dedd 100644 --- a/pkg/worker/caged/libretro/frontend.go +++ b/pkg/worker/caged/libretro/frontend.go @@ -66,7 +66,8 @@ type Frontend struct { th int // draw threads vw, vh int // out frame size - mu sync.Mutex + mu sync.Mutex + mui sync.Mutex DisableCanvasPool bool SaveOnClose bool @@ -198,7 +199,7 @@ func (f *Frontend) Shutdown() { f.SetAudioCb(noAudio) f.SetVideoCb(noVideo) f.mu.Unlock() - f.log.Debug().Msgf("frontend closed") + f.log.Debug().Msgf("frontend shutdown done") } func (f *Frontend) linkNano(nano *nanoarch.Nanoarch) { @@ -214,11 +215,14 @@ func (f *Frontend) linkNano(nano *nanoarch.Nanoarch) { func (f *Frontend) SetOnAV(fn func()) { f.nano.OnSystemAvInfo = fn } func (f *Frontend) Start() { - f.log.Debug().Msgf("Frontend start") + f.log.Debug().Msgf("frontend start") f.done = make(chan struct{}) f.nano.LastFrameTime = time.Now().UnixNano() + + f.mui.Lock() defer f.Shutdown() + defer f.mui.Unlock() if f.HasSave() { // advance 1 frame for Mupen save state @@ -248,34 +252,28 @@ func (f *Frontend) Start() { } } -func (f *Frontend) PixFormat() uint32 { return f.nano.Video.PixFmt.C } -func (f *Frontend) Rotation() uint { return f.nano.Rot } +func (f *Frontend) AudioSampleRate() int { return f.nano.AudioSampleRate() } +func (f *Frontend) FPS() int { return f.nano.VideoFramerate() } func (f *Frontend) Flipped() bool { return f.nano.IsGL() } func (f *Frontend) FrameSize() (int, int) { return f.nano.GeometryBase() } -func (f *Frontend) FPS() int { return f.nano.VideoFramerate() } -func (f *Frontend) HashPath() string { return f.storage.GetSavePath() } func (f *Frontend) HasSave() bool { return os.Exists(f.HashPath()) } -func (f *Frontend) SRAMPath() string { return f.storage.GetSRAMPath() } -func (f *Frontend) AudioSampleRate() int { return f.nano.AudioSampleRate() } +func (f *Frontend) HashPath() string { return f.storage.GetSavePath() } func (f *Frontend) Input(player int, data []byte) { f.input.setInput(player, data) } +func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() } func (f *Frontend) LoadGame(path string) error { return f.nano.LoadGame(path) } +func (f *Frontend) PixFormat() uint32 { return f.nano.Video.PixFmt.C } func (f *Frontend) RestoreGameState() error { return f.Load() } -func (f *Frontend) Scale() float64 { return f.scale } -func (f *Frontend) IsPortrait() bool { return f.nano.IsPortrait() } +func (f *Frontend) Rotation() uint { return f.nano.Rot } +func (f *Frontend) SRAMPath() string { return f.storage.GetSRAMPath() } func (f *Frontend) SaveGameState() error { return f.Save() } +func (f *Frontend) Scale() float64 { return f.scale } func (f *Frontend) SetAudioCb(cb func(app.Audio)) { f.onAudio = cb } func (f *Frontend) SetSessionId(name string) { f.storage.SetMainSaveName(name) } func (f *Frontend) SetVideoCb(ff func(app.Video)) { f.onVideo = ff } -func (f *Frontend) SetViewport(width int, height int) { - f.mu.Lock() - f.vw, f.vh = width, height - f.mu.Unlock() -} - -// Tick runs one emulation frame. -func (f *Frontend) Tick() { f.mu.Lock(); f.nano.Run(); f.mu.Unlock() } -func (f *Frontend) ToggleMultitap() { f.nano.ToggleMultitap() } -func (f *Frontend) ViewportSize() (int, int) { return f.vw, f.vh } +func (f *Frontend) SetViewport(w, h int) { f.mu.Lock(); f.vw, f.vh = w, h; f.mu.Unlock() } +func (f *Frontend) Tick() { f.mu.Lock(); f.nano.Run(); f.mu.Unlock() } +func (f *Frontend) ToggleMultitap() { f.nano.ToggleMultitap() } +func (f *Frontend) ViewportSize() (int, int) { return f.vw, f.vh } func (f *Frontend) ViewportCalc() (nw int, nh int) { w, h := f.FrameSize() @@ -307,8 +305,11 @@ func (f *Frontend) ViewportCalc() (nw int, nh int) { } func (f *Frontend) Close() { - f.log.Debug().Msgf("frontend close called") + f.log.Debug().Msgf("frontend close") + close(f.done) + f.mui.Lock() + defer f.mui.Unlock() // Save game on quit if it was saved before (shared or click-saved). if f.SaveOnClose && f.HasSave() { f.log.Debug().Msg("Save on quit") @@ -316,9 +317,8 @@ func (f *Frontend) Close() { f.log.Error().Err(err).Msg("save on quit failed") } } - - close(f.done) f.nano.Close() + f.log.Debug().Msgf("frontend closed") } // Save writes the current state to the filesystem. diff --git a/pkg/worker/caged/libretro/frontend_test.go b/pkg/worker/caged/libretro/frontend_test.go index 0fa88f104..7c13cb76d 100644 --- a/pkg/worker/caged/libretro/frontend_test.go +++ b/pkg/worker/caged/libretro/frontend_test.go @@ -10,6 +10,7 @@ import ( "path/filepath" "sync" "testing" + "time" "github.com/giongto35/cloud-game/v3/pkg/config" "github.com/giongto35/cloud-game/v3/pkg/logger" @@ -67,9 +68,9 @@ func EmulatorMock(room string, system string) *TestFrontend { conf.Emulator.Storage = expand("tests", "storage") l := logger.Default() - l2 := l.Extend(l.Level(logger.ErrorLevel).With()) + l2 := l.Extend(l.Level(logger.WarnLevel).With()) - if err := manager.CheckCores(conf.Emulator, l); err != nil { + if err := manager.CheckCores(conf.Emulator, l2); err != nil { panic(err) } @@ -354,6 +355,18 @@ func TestConcurrentInput(t *testing.T) { wg.Wait() } +func TestStartStop(t *testing.T) { + f1 := DefaultFrontend("sushi", sushi.system, sushi.rom) + go f1.Start() + time.Sleep(1 * time.Second) + f1.Close() + + f2 := DefaultFrontend("sushi", sushi.system, sushi.rom) + go f2.Start() + time.Sleep(100 * time.Millisecond) + f2.Close() +} + // expand joins a list of file path elements. func expand(p ...string) string { ph, _ := filepath.Abs(filepath.FromSlash(filepath.Join(p...))) diff --git a/pkg/worker/coordinatorhandlers.go b/pkg/worker/coordinatorhandlers.go index 970f023db..7384c8c67 100644 --- a/pkg/worker/coordinatorhandlers.go +++ b/pkg/worker/coordinatorhandlers.go @@ -91,7 +91,7 @@ func (c *coordinator) HandleGameStart(rq api.StartGameRequest[com.Uid], w *Worke r = room.NewRoom[*room.GameSession](uid, nil, w.router.Users(), nil) r.HandleClose = func() { c.CloseRoom(uid) - c.log.Debug().Msgf("room close request %v sent") + c.log.Debug().Msgf("room close request %v sent", uid) } if other := w.router.Room(); other != nil {