audio: pair locks with defer and move the disposal of a context to audio.go

This commit is contained in:
dualfroz committed 2026-10-04 12:56:43 +02:00
1 parent 8198c5c4b3
commit 4300b92d4b
3 files changed
+35 -24

No files matched your search

+13
View File
@@ -233,6 +233,19 @@ func (c *Context) waitForPlayingPlayers() bool {
return !c.disposed return !c.disposed
} }
// dispose makes the goroutine updating the players exit, and waits for it to exit.
func (c *Context) dispose() {
c.markDisposed()
c.updater.Wait()
}
func (c *Context) markDisposed() {
c.m.Lock()
defer c.m.Unlock()
c.disposed = true
c.cond.Broadcast()
}
func (c *Context) addPlayingPlayer(p *playerImpl) { func (c *Context) addPlayingPlayer(p *playerImpl) {
c.m.Lock() c.m.Lock()
defer c.m.Unlock() defer c.m.Unlock()
+2 -5
View File
@@ -71,8 +71,7 @@ func TestGC(t *testing.T) {
if err := audio.UpdateForTesting(); err != nil { if err := audio.UpdateForTesting(); err != nil {
t.Error(err) t.Error(err)
} }
// 200[ms] should be enough for all the bytes to be consumed. The test runs on the fake // 200[ms] should be enough for all the bytes to be consumed.
// clock of synctest, so this does not take real time.
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
} }
t.Errorf("time out") t.Errorf("time out")
@@ -658,8 +657,7 @@ func TestPositionNotGrowingAfterFinished(t *testing.T) {
} }
p.Play() p.Play()
// Wait until the player finishes its source. The test runs on the fake clock of synctest, so // Wait until the player finishes its source.
// the deadline does not take real time.
var finished bool var finished bool
deadline := time.Now().Add(10 * time.Second) deadline := time.Now().Add(10 * time.Second)
for time.Now().Before(deadline) { for time.Now().Before(deadline) {
@@ -889,7 +887,6 @@ func TestPlayOnIdleContext(t *testing.T) {
} }
// waitUntil polls cond until it holds or the deadline passes, and reports whether it held. // waitUntil polls cond until it holds or the deadline passes, and reports whether it held.
// The test runs on the fake clock of synctest, so the deadline does not take real time.
waitUntil := func(cond func() bool) bool { waitUntil := func(cond func() bool) bool {
deadline := time.Now().Add(10 * time.Second) deadline := time.Now().Add(10 * time.Second)
for time.Now().Before(deadline) { for time.Now().Before(deadline) {
+20 -19
View File
@@ -73,17 +73,20 @@ func (c *dummyContext) NewPlayer(r io.Reader) player {
// stopPlayers stops the players' goroutines reading their sources, and waits for them to exit. // stopPlayers stops the players' goroutines reading their sources, and waits for them to exit.
func (c *dummyContext) stopPlayers() { func (c *dummyContext) stopPlayers() {
c.mu.Lock() for _, p := range c.takePlayers() {
players := c.players
c.players = nil
c.mu.Unlock()
for _, p := range players {
p.PauseAndStopReading() p.PauseAndStopReading()
p.readers.Wait() p.readers.Wait()
} }
} }
func (c *dummyContext) takePlayers() []*dummyPlayer {
c.mu.Lock()
defer c.mu.Unlock()
players := c.players
c.players = nil
return players
}
func (c *dummyContext) MaxBufferSize() int { func (c *dummyContext) MaxBufferSize() int {
return 48000 * channelCount * bitDepthInBytesInt16 / 4 return 48000 * channelCount * bitDepthInBytesInt16 / 4
} }
@@ -349,23 +352,21 @@ func ContextCreatedForTesting() bool {
} }
// ResetContextForTesting discards the current context, so that a test can create a new one. // ResetContextForTesting discards the current context, so that a test can create a new one.
// ResetContextForTesting waits for the goroutines the context and its players started to exit, so // ResetContextForTesting stops the goroutine updating the players and the goroutines reading the
// that they do not outlive the test. // players' sources, and waits for them to exit.
func ResetContextForTesting() { func ResetContextForTesting() {
if c := takeContextForTesting(); c != nil {
c.dispose()
}
dummyContextForTesting.stopPlayers()
}
func takeContextForTesting() *Context {
theContextLock.Lock() theContextLock.Lock()
defer theContextLock.Unlock()
c := theContext c := theContext
theContext = nil theContext = nil
theContextLock.Unlock() return c
if c != nil {
c.m.Lock()
c.disposed = true
c.cond.Broadcast()
c.m.Unlock()
c.updater.Wait()
}
dummyContextForTesting.stopPlayers()
} }
func (i *InfiniteLoop) SetNoBlendForTesting(value bool) { func (i *InfiniteLoop) SetNoBlendForTesting(value bool) {