diff --git a/audio/internal/readerdriver/driver_windows.go b/audio/internal/readerdriver/driver_windows.go index 1b1e1b5ae..864a33e76 100644 --- a/audio/internal/readerdriver/driver_windows.go +++ b/audio/internal/readerdriver/driver_windows.go @@ -18,6 +18,7 @@ import ( "io" "runtime" "sync" + "sync/atomic" "unsafe" "golang.org/x/sys/windows" @@ -97,32 +98,32 @@ type players struct { context *context players map[*playerImpl]struct{} buf []byte - err error + err atomic.Value waveOut uintptr headers []*header + // cond protects only the member 'players'. + // The other members don't have to be protected. cond *sync.Cond } func (p *players) setContext(context *context) { - p.cond.L.Lock() - defer p.cond.L.Unlock() p.context = context } func (p *players) add(player *playerImpl) error { - p.cond.L.Lock() - defer p.cond.L.Unlock() - - if p.err != nil { - return p.err + if err := p.err.Load(); err != nil { + return err.(error) } + p.cond.L.Lock() if p.players == nil { p.players = map[*playerImpl]struct{}{} } p.players[player] = struct{}{} + p.cond.L.Unlock() + p.cond.Signal() if p.waveOut != 0 { @@ -160,7 +161,8 @@ func (p *players) add(player *playerImpl) error { p.headers = append(p.headers, h) } - if err := p.readAndWriteBuffers(); err != nil { + // There should be only one players in total. + if err := p.readAndWriteBuffers([]*playerImpl{player}); err != nil { return err } @@ -170,16 +172,14 @@ func (p *players) add(player *playerImpl) error { } func (p *players) remove(player *playerImpl) error { - p.cond.L.Lock() - defer p.cond.L.Unlock() - return p.removeImpl(player) -} - -func (p *players) removeImpl(player *playerImpl) error { - if p.err != nil { - return p.err + if err := p.err.Load(); err != nil { + return err.(error) } + + p.cond.L.Lock() delete(p.players, player) + p.cond.L.Unlock() + return nil } @@ -206,28 +206,30 @@ func (p *players) shouldWait() bool { } func (p *players) loop() { + var players []*playerImpl for { p.cond.L.Lock() for p.shouldWait() { p.cond.Wait() } - if p.waveOut == 0 { - p.cond.L.Unlock() - return - } - if err := p.readAndWriteBuffers(); err != nil { - p.err = err - p.cond.L.Unlock() - break + + players = players[:0] + for pl := range p.players { + players = append(players, pl) } p.cond.L.Unlock() + + if p.waveOut == 0 { + return + } + if err := p.readAndWriteBuffers(players); err != nil { + p.err.Store(err) + break + } } } func (p *players) suspend() error { - p.cond.L.Lock() - defer p.cond.L.Unlock() - if p.waveOut == 0 { return nil } @@ -238,9 +240,6 @@ func (p *players) suspend() error { } func (p *players) resume() error { - p.cond.L.Lock() - defer p.cond.L.Unlock() - if p.waveOut == 0 { return nil } @@ -260,8 +259,8 @@ var waveOutOpenCallback = windows.NewCallbackCDecl(func(hwo, uMsg, dwInstance, d return 0 }) -func (p *players) readAndWriteBuffers() error { - if len(p.players) == 0 { +func (p *players) readAndWriteBuffers(players []*playerImpl) error { + if len(players) == 0 { return nil } @@ -283,7 +282,7 @@ func (p *players) readAndWriteBuffers() error { // waveOutSetVolume is not used since it doesn't work correctly in some environments. var volumes []float64 var bufs [][]byte - for pl := range p.players { + for _, pl := range players { buf := make([]byte, n) n := pl.read(buf) bufs = append(bufs, buf[:n])