audio: Bug fix: Seek might cause dead lock after Close

This commit is contained in:
Hajime Hoshi 2017-12-23 18:29:43 +09:00
parent db77658935
commit 04739a7249

View File

@ -36,6 +36,7 @@ package audio
import (
"bytes"
"errors"
"fmt"
"io"
"runtime"
"sync"
@ -331,7 +332,8 @@ type Player struct {
volume float64
closeCh chan struct{}
closedCh chan error
closedCh chan struct{}
readLoopEndedCh chan struct{}
seekCh chan seekArgs
seekedCh chan error
proceedCh chan []int16
@ -371,7 +373,8 @@ func NewPlayer(context *Context, src ReadSeekCloser) (*Player, error) {
buf: []byte{},
volume: 1,
closeCh: make(chan struct{}),
closedCh: make(chan error),
closedCh: make(chan struct{}),
readLoopEndedCh: make(chan struct{}),
seekCh: make(chan seekArgs),
seekedCh: make(chan error),
proceedCh: make(chan []int16),
@ -419,14 +422,23 @@ func (p *Player) Close() error {
runtime.SetFinalizer(p, nil)
p.players.removePlayer(p)
p.closeCh <- struct{}{}
return <-p.closedCh
select {
case p.closeCh <- struct{}{}:
<-p.closedCh
return nil
case <-p.readLoopEndedCh:
return fmt.Errorf("audio: the player is already closed")
}
}
func (p *Player) bufferToInt16(lengthInBytes int) ([]int16, error) {
p.proceedCh <- make([]int16, lengthInBytes/2)
select {
case p.proceedCh <- make([]int16, lengthInBytes/2):
r := <-p.proceededCh
return r.buf, r.err
case <-p.readLoopEndedCh:
return nil, fmt.Errorf("audio: the player is already closed")
}
}
// Play plays the stream.
@ -438,12 +450,18 @@ func (p *Player) Play() error {
}
func (p *Player) readLoop() {
defer func() {
// Note: the error is ignored
p.src.Close()
close(p.readLoopEndedCh)
}()
t := time.After(0)
var readErr error
for {
select {
case <-p.closeCh:
p.closedCh <- p.src.Close()
p.closedCh <- struct{}{}
return
case s := <-p.seekCh:
@ -549,8 +567,12 @@ func (p *Player) Rewind() error {
func (p *Player) Seek(offset time.Duration) error {
o := int64(offset) * bytesPerSample * channelNum * int64(p.sampleRate) / int64(time.Second)
o &= mask
p.seekCh <- seekArgs{o, io.SeekStart}
select {
case p.seekCh <- seekArgs{o, io.SeekStart}:
return <-p.seekedCh
case <-p.readLoopEndedCh:
return fmt.Errorf("audio: the player is already closed")
}
}
// Pause pauses the playing.