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