From d10636d235e3a9069254d95f600f780ca6c5632c Mon Sep 17 00:00:00 2001 From: Hajime Hoshi Date: Thu, 1 Feb 2024 20:28:52 +0900 Subject: [PATCH] audio: bug fix: suspend and resume player's stopwatches Updates #2901 --- audio/audio.go | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ audio/player.go | 22 ++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/audio/audio.go b/audio/audio.go index ee3a0ef2c..d6ba8cc3b 100644 --- a/audio/audio.go +++ b/audio/audio.go @@ -111,6 +111,9 @@ func NewContext(sampleRate int) *Context { if err := c.playerFactory.suspend(); err != nil { return err } + if err := c.onSuspend(); err != nil { + return err + } return nil }) h.OnResumeAudio(func() error { @@ -118,6 +121,9 @@ func NewContext(sampleRate int) *Context { if err := c.playerFactory.resume(); err != nil { return err } + if err := c.onResume(); err != nil { + return err + } return nil }) @@ -197,6 +203,48 @@ func (c *Context) removePlayingPlayer(p *playerImpl) { c.m.Unlock() } +func (c *Context) onSuspend() error { + // A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737). + // Copy the playerImpls and iterate them without a lock. + var players []*playerImpl + c.m.Lock() + players = make([]*playerImpl, 0, len(c.playingPlayers)) + for p := range c.playingPlayers { + players = append(players, p) + } + c.m.Unlock() + + for _, p := range players { + if err := p.Err(); err != nil { + return err + } + p.onContextSuspended() + } + + return nil +} + +func (c *Context) onResume() error { + // A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737). + // Copy the playerImpls and iterate them without a lock. + var players []*playerImpl + c.m.Lock() + players = make([]*playerImpl, 0, len(c.playingPlayers)) + for p := range c.playingPlayers { + players = append(players, p) + } + c.m.Unlock() + + for _, p := range players { + if err := p.Err(); err != nil { + return err + } + p.onContextResumed() + } + + return nil +} + func (c *Context) updatePlayers() error { // A Context must not call playerImpl's functions with a lock, or this causes a deadlock (#2737). // Copy the playerImpls and iterate them without a lock. diff --git a/audio/player.go b/audio/player.go index bae4b61d5..89d6a3be7 100644 --- a/audio/player.go +++ b/audio/player.go @@ -326,6 +326,28 @@ func (p *playerImpl) source() io.Reader { return p.src } +func (p *playerImpl) onContextSuspended() { + p.m.Lock() + defer p.m.Unlock() + + if p.player == nil { + return + } + p.stopwatch.stop() +} + +func (p *playerImpl) onContextResumed() { + p.m.Lock() + defer p.m.Unlock() + + if p.player == nil { + return + } + if p.isPlaying() { + p.stopwatch.start() + } +} + func (p *playerImpl) updatePosition() { p.m.Lock() defer p.m.Unlock()