forked from External/mediamtx
use contexts anywhere is possible
This commit is contained in:
parent
6d02ec9478
commit
e558b245e7
12 changed files with 285 additions and 342 deletions
2
go.mod
2
go.mod
|
|
@ -5,7 +5,7 @@ go 1.15
|
||||||
require (
|
require (
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||||
github.com/aler9/gortsplib v0.0.0-20210510211300-0d6385640fad
|
github.com/aler9/gortsplib v0.0.0-20210511094934-fa2830eb2251
|
||||||
github.com/asticode/go-astits v0.0.0-00010101000000-000000000000
|
github.com/asticode/go-astits v0.0.0-00010101000000-000000000000
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -4,8 +4,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2c
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/aler9/go-astits v0.0.0-20210423195926-582b09ed7c04 h1:CXgQLsU4uxWAmsXNOjGLbj0A+0IlRcpZpMgI13fmVwo=
|
github.com/aler9/go-astits v0.0.0-20210423195926-582b09ed7c04 h1:CXgQLsU4uxWAmsXNOjGLbj0A+0IlRcpZpMgI13fmVwo=
|
||||||
github.com/aler9/go-astits v0.0.0-20210423195926-582b09ed7c04/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
|
github.com/aler9/go-astits v0.0.0-20210423195926-582b09ed7c04/go.mod h1:DkOWmBNQpnr9mv24KfZjq4JawCFX1FCqjLVGvO0DygQ=
|
||||||
github.com/aler9/gortsplib v0.0.0-20210510211300-0d6385640fad h1:u6DNZKoG3gm5pMz+K1duYS5pqbrq6iuiDB1kJc4N0d4=
|
github.com/aler9/gortsplib v0.0.0-20210511094934-fa2830eb2251 h1:pG9MnD5MOIpqcOBccQ5L4ZAhJGBcN7LKO87ilADzIYI=
|
||||||
github.com/aler9/gortsplib v0.0.0-20210510211300-0d6385640fad/go.mod h1:zVCg+TQX445hh1pC5QgAuuBvvXZMWLY1XYz626dGFqY=
|
github.com/aler9/gortsplib v0.0.0-20210511094934-fa2830eb2251/go.mod h1:zVCg+TQX445hh1pC5QgAuuBvvXZMWLY1XYz626dGFqY=
|
||||||
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927 h1:95mXJ5fUCYpBRdSOnLAQAdJHHKxxxJrVCiaqDi965YQ=
|
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927 h1:95mXJ5fUCYpBRdSOnLAQAdJHHKxxxJrVCiaqDi965YQ=
|
||||||
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927/go.mod h1:vzuE21rowz+lT1NGsWbreIvYulgBpCGnQyeTyFblUHc=
|
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927/go.mod h1:vzuE21rowz+lT1NGsWbreIvYulgBpCGnQyeTyFblUHc=
|
||||||
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=
|
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,8 @@ type Converter struct {
|
||||||
pathMan PathMan
|
pathMan PathMan
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel func()
|
||||||
path readpublisher.Path
|
path readpublisher.Path
|
||||||
ringBuffer *ringbuffer.RingBuffer
|
ringBuffer *ringbuffer.RingBuffer
|
||||||
tsQueue []*tsFile
|
tsQueue []*tsFile
|
||||||
|
|
@ -145,9 +147,7 @@ type Converter struct {
|
||||||
lastRequestTime int64
|
lastRequestTime int64
|
||||||
|
|
||||||
// in
|
// in
|
||||||
request chan Request
|
request chan Request
|
||||||
terminate chan struct{}
|
|
||||||
parentTerminate chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Converter.
|
// New allocates a Converter.
|
||||||
|
|
@ -161,6 +161,8 @@ func New(
|
||||||
pathMan PathMan,
|
pathMan PathMan,
|
||||||
parent Parent) *Converter {
|
parent Parent) *Converter {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
c := &Converter{
|
c := &Converter{
|
||||||
hlsSegmentCount: hlsSegmentCount,
|
hlsSegmentCount: hlsSegmentCount,
|
||||||
hlsSegmentDuration: hlsSegmentDuration,
|
hlsSegmentDuration: hlsSegmentDuration,
|
||||||
|
|
@ -170,11 +172,11 @@ func New(
|
||||||
pathName: pathName,
|
pathName: pathName,
|
||||||
pathMan: pathMan,
|
pathMan: pathMan,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
lastRequestTime: time.Now().Unix(),
|
lastRequestTime: time.Now().Unix(),
|
||||||
tsByName: make(map[string]*tsFile),
|
tsByName: make(map[string]*tsFile),
|
||||||
request: make(chan Request),
|
request: make(chan Request),
|
||||||
terminate: make(chan struct{}, 1),
|
|
||||||
parentTerminate: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log(logger.Info, "opened")
|
c.log(logger.Info, "opened")
|
||||||
|
|
@ -188,15 +190,11 @@ func New(
|
||||||
// ParentClose closes a Converter.
|
// ParentClose closes a Converter.
|
||||||
func (c *Converter) ParentClose() {
|
func (c *Converter) ParentClose() {
|
||||||
c.log(logger.Info, "closed")
|
c.log(logger.Info, "closed")
|
||||||
close(c.parentTerminate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a Converter.
|
// Close closes a Converter.
|
||||||
func (c *Converter) Close() {
|
func (c *Converter) Close() {
|
||||||
select {
|
c.ctxCancel()
|
||||||
case c.terminate <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsReadPublisher implements readpublisher.ReadPublisher.
|
// IsReadPublisher implements readpublisher.ReadPublisher.
|
||||||
|
|
@ -217,28 +215,23 @@ func (c *Converter) PathName() string {
|
||||||
func (c *Converter) run() {
|
func (c *Converter) run() {
|
||||||
defer c.wg.Done()
|
defer c.wg.Done()
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
innerCtx, innerCtxCancel := context.WithCancel(context.Background())
|
||||||
runErr := make(chan error)
|
runErr := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
runErr <- c.runInner(ctx)
|
runErr <- c.runInner(innerCtx)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case err := <-runErr:
|
case err := <-runErr:
|
||||||
cancel()
|
innerCtxCancel()
|
||||||
c.log(logger.Info, "ERR: %s", err)
|
c.log(logger.Info, "ERR: %s", err)
|
||||||
|
|
||||||
case <-c.terminate:
|
case <-c.ctx.Done():
|
||||||
cancel()
|
innerCtxCancel()
|
||||||
<-runErr
|
<-runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
c.ctxCancel()
|
||||||
for req := range c.request {
|
|
||||||
req.W.WriteHeader(http.StatusNotFound)
|
|
||||||
req.Res <- nil
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if c.path != nil {
|
if c.path != nil {
|
||||||
res := make(chan struct{})
|
res := make(chan struct{})
|
||||||
|
|
@ -247,12 +240,9 @@ func (c *Converter) run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.parent.OnConverterClose(c)
|
c.parent.OnConverterClose(c)
|
||||||
<-c.parentTerminate
|
|
||||||
|
|
||||||
close(c.request)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Converter) runInner(ctx context.Context) error {
|
func (c *Converter) runInner(innerCtx context.Context) error {
|
||||||
pres := make(chan readpublisher.SetupPlayRes)
|
pres := make(chan readpublisher.SetupPlayRes)
|
||||||
c.pathMan.OnReadPublisherSetupPlay(readpublisher.SetupPlayReq{
|
c.pathMan.OnReadPublisherSetupPlay(readpublisher.SetupPlayReq{
|
||||||
Author: c,
|
Author: c,
|
||||||
|
|
@ -504,7 +494,7 @@ func (c *Converter) runInner(ctx context.Context) error {
|
||||||
case err := <-writerDone:
|
case err := <-writerDone:
|
||||||
return err
|
return err
|
||||||
|
|
||||||
case <-ctx.Done():
|
case <-innerCtx.Done():
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -598,7 +588,12 @@ func (c *Converter) runRequestHandler(terminate chan struct{}, done chan struct{
|
||||||
|
|
||||||
// OnRequest is called by hlsserver.Server.
|
// OnRequest is called by hlsserver.Server.
|
||||||
func (c *Converter) OnRequest(req Request) {
|
func (c *Converter) OnRequest(req Request) {
|
||||||
c.request <- req
|
select {
|
||||||
|
case c.request <- req:
|
||||||
|
case <-c.ctx.Done():
|
||||||
|
req.W.WriteHeader(http.StatusNotFound)
|
||||||
|
req.Res <- nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFrame implements path.Reader.
|
// OnFrame implements path.Reader.
|
||||||
|
|
|
||||||
|
|
@ -29,17 +29,15 @@ type Server struct {
|
||||||
pathMan *pathman.PathManager
|
pathMan *pathman.PathManager
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
ln net.Listener
|
ctx context.Context
|
||||||
|
ctxCancel func()
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
|
ln net.Listener
|
||||||
converters map[string]*hlsconverter.Converter
|
converters map[string]*hlsconverter.Converter
|
||||||
|
|
||||||
// in
|
// in
|
||||||
request chan hlsconverter.Request
|
request chan hlsconverter.Request
|
||||||
connClose chan *hlsconverter.Converter
|
connClose chan *hlsconverter.Converter
|
||||||
terminate chan struct{}
|
|
||||||
|
|
||||||
// out
|
|
||||||
done chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Server.
|
// New allocates a Server.
|
||||||
|
|
@ -58,6 +56,8 @@ func New(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
hlsSegmentCount: hlsSegmentCount,
|
hlsSegmentCount: hlsSegmentCount,
|
||||||
hlsSegmentDuration: hlsSegmentDuration,
|
hlsSegmentDuration: hlsSegmentDuration,
|
||||||
|
|
@ -65,16 +65,17 @@ func New(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
pathMan: pathMan,
|
pathMan: pathMan,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
ln: ln,
|
ln: ln,
|
||||||
converters: make(map[string]*hlsconverter.Converter),
|
converters: make(map[string]*hlsconverter.Converter),
|
||||||
request: make(chan hlsconverter.Request),
|
request: make(chan hlsconverter.Request),
|
||||||
connClose: make(chan *hlsconverter.Converter),
|
connClose: make(chan *hlsconverter.Converter),
|
||||||
terminate: make(chan struct{}),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Log(logger.Info, "listener opened on "+address)
|
s.Log(logger.Info, "listener opened on "+address)
|
||||||
|
|
||||||
|
s.wg.Add(1)
|
||||||
go s.run()
|
go s.run()
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
@ -87,12 +88,12 @@ func (s *Server) Log(level logger.Level, format string, args ...interface{}) {
|
||||||
|
|
||||||
// Close closes all the server resources.
|
// Close closes all the server resources.
|
||||||
func (s *Server) Close() {
|
func (s *Server) Close() {
|
||||||
close(s.terminate)
|
s.ctxCancel()
|
||||||
<-s.done
|
s.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) run() {
|
func (s *Server) run() {
|
||||||
defer close(s.done)
|
defer s.wg.Done()
|
||||||
|
|
||||||
hs := &http.Server{Handler: s}
|
hs := &http.Server{Handler: s}
|
||||||
go hs.Serve(s.ln)
|
go hs.Serve(s.ln)
|
||||||
|
|
@ -122,36 +123,18 @@ outer:
|
||||||
}
|
}
|
||||||
s.doConverterClose(c)
|
s.doConverterClose(c)
|
||||||
|
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
s.ctxCancel()
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case req, ok := <-s.request:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- nil
|
|
||||||
|
|
||||||
case _, ok := <-s.connClose:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, c := range s.converters {
|
for _, c := range s.converters {
|
||||||
s.doConverterClose(c)
|
s.doConverterClose(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
hs.Shutdown(context.Background())
|
hs.Shutdown(context.Background())
|
||||||
|
|
||||||
close(s.request)
|
|
||||||
close(s.connClose)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServeHTTP implements http.Handler.
|
// ServeHTTP implements http.Handler.
|
||||||
|
|
@ -174,30 +157,36 @@ func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
cres := make(chan io.Reader)
|
cres := make(chan io.Reader)
|
||||||
s.request <- hlsconverter.Request{
|
hreq := hlsconverter.Request{
|
||||||
Path: parts[0],
|
Path: parts[0],
|
||||||
Subpath: parts[1],
|
Subpath: parts[1],
|
||||||
Req: r,
|
Req: r,
|
||||||
W: w,
|
W: w,
|
||||||
Res: cres,
|
Res: cres,
|
||||||
}
|
}
|
||||||
res := <-cres
|
|
||||||
|
|
||||||
if res != nil {
|
select {
|
||||||
buf := make([]byte, 4096)
|
case s.request <- hreq:
|
||||||
for {
|
res := <-cres
|
||||||
n, err := res.Read(buf)
|
|
||||||
if err != nil {
|
if res != nil {
|
||||||
return
|
buf := make([]byte, 4096)
|
||||||
|
for {
|
||||||
|
n, err := res.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = w.Write(buf[:n])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.(http.Flusher).Flush()
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = w.Write(buf[:n])
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
w.(http.Flusher).Flush()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case <-s.ctx.Done():
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -208,5 +197,8 @@ func (s *Server) doConverterClose(c *hlsconverter.Converter) {
|
||||||
|
|
||||||
// OnConverterClose is called by hlsconverter.Converter.
|
// OnConverterClose is called by hlsconverter.Converter.
|
||||||
func (s *Server) OnConverterClose(c *hlsconverter.Converter) {
|
func (s *Server) OnConverterClose(c *hlsconverter.Converter) {
|
||||||
s.connClose <- c
|
select {
|
||||||
|
case s.connClose <- c:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package path
|
package path
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -70,8 +71,9 @@ type Path struct {
|
||||||
stats *stats.Stats
|
stats *stats.Stats
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel func()
|
||||||
readPublishers map[readpublisher.ReadPublisher]readPublisherState
|
readPublishers map[readpublisher.ReadPublisher]readPublisherState
|
||||||
readPublishersWg sync.WaitGroup
|
|
||||||
describeRequests []readpublisher.DescribeReq
|
describeRequests []readpublisher.DescribeReq
|
||||||
setupPlayRequests []readpublisher.SetupPlayReq
|
setupPlayRequests []readpublisher.SetupPlayReq
|
||||||
source source.Source
|
source source.Source
|
||||||
|
|
@ -99,7 +101,6 @@ type Path struct {
|
||||||
recordReq chan readpublisher.RecordReq
|
recordReq chan readpublisher.RecordReq
|
||||||
pauseReq chan readpublisher.PauseReq
|
pauseReq chan readpublisher.PauseReq
|
||||||
removeReq chan readpublisher.RemoveReq
|
removeReq chan readpublisher.RemoveReq
|
||||||
terminate chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Path.
|
// New allocates a Path.
|
||||||
|
|
@ -116,6 +117,8 @@ func New(
|
||||||
stats *stats.Stats,
|
stats *stats.Stats,
|
||||||
parent Parent) *Path {
|
parent Parent) *Path {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
pa := &Path{
|
pa := &Path{
|
||||||
rtspAddress: rtspAddress,
|
rtspAddress: rtspAddress,
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
|
|
@ -128,6 +131,8 @@ func New(
|
||||||
wg: wg,
|
wg: wg,
|
||||||
stats: stats,
|
stats: stats,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
readPublishers: make(map[readpublisher.ReadPublisher]readPublisherState),
|
readPublishers: make(map[readpublisher.ReadPublisher]readPublisherState),
|
||||||
readers: newReadersMap(),
|
readers: newReadersMap(),
|
||||||
describeTimer: newEmptyTimer(),
|
describeTimer: newEmptyTimer(),
|
||||||
|
|
@ -143,7 +148,6 @@ func New(
|
||||||
recordReq: make(chan readpublisher.RecordReq),
|
recordReq: make(chan readpublisher.RecordReq),
|
||||||
pauseReq: make(chan readpublisher.PauseReq),
|
pauseReq: make(chan readpublisher.PauseReq),
|
||||||
removeReq: make(chan readpublisher.RemoveReq),
|
removeReq: make(chan readpublisher.RemoveReq),
|
||||||
terminate: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pa.wg.Add(1)
|
pa.wg.Add(1)
|
||||||
|
|
@ -153,7 +157,7 @@ func New(
|
||||||
|
|
||||||
// Close closes a path.
|
// Close closes a path.
|
||||||
func (pa *Path) Close() {
|
func (pa *Path) Close() {
|
||||||
close(pa.terminate)
|
pa.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log is the main logging function.
|
// Log is the main logging function.
|
||||||
|
|
@ -218,9 +222,6 @@ outer:
|
||||||
pa.scheduleClose()
|
pa.scheduleClose()
|
||||||
|
|
||||||
case <-pa.closeTimer.C:
|
case <-pa.closeTimer.C:
|
||||||
pa.exhaustChannels()
|
|
||||||
pa.parent.OnPathClose(pa)
|
|
||||||
<-pa.terminate
|
|
||||||
break outer
|
break outer
|
||||||
|
|
||||||
case req := <-pa.extSourceSetReady:
|
case req := <-pa.extSourceSetReady:
|
||||||
|
|
@ -262,15 +263,15 @@ outer:
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(pa.readPublishers, req.Author)
|
delete(pa.readPublishers, req.Author)
|
||||||
pa.readPublishersWg.Done()
|
|
||||||
close(req.Res)
|
close(req.Res)
|
||||||
|
|
||||||
case <-pa.terminate:
|
case <-pa.ctx.Done():
|
||||||
pa.exhaustChannels()
|
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pa.ctxCancel()
|
||||||
|
|
||||||
pa.describeTimer.Stop()
|
pa.describeTimer.Stop()
|
||||||
pa.sourceCloseTimer.Stop()
|
pa.sourceCloseTimer.Stop()
|
||||||
pa.runOnDemandCloseTimer.Stop()
|
pa.runOnDemandCloseTimer.Stop()
|
||||||
|
|
@ -312,86 +313,8 @@ outer:
|
||||||
c.Close()
|
c.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pa.readPublishersWg.Wait()
|
|
||||||
|
|
||||||
close(pa.extSourceSetReady)
|
pa.parent.OnPathClose(pa)
|
||||||
close(pa.extSourceSetNotReady)
|
|
||||||
close(pa.describeReq)
|
|
||||||
close(pa.setupPlayReq)
|
|
||||||
close(pa.announceReq)
|
|
||||||
close(pa.playReq)
|
|
||||||
close(pa.recordReq)
|
|
||||||
close(pa.pauseReq)
|
|
||||||
close(pa.removeReq)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pa *Path) exhaustChannels() {
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case req, ok := <-pa.extSourceSetReady:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(req.Res)
|
|
||||||
|
|
||||||
case req, ok := <-pa.extSourceSetNotReady:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(req.Res)
|
|
||||||
|
|
||||||
case req, ok := <-pa.describeReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.DescribeRes{nil, "", fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
|
|
||||||
case req, ok := <-pa.setupPlayReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.SetupPlayRes{nil, nil, fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
|
|
||||||
case req, ok := <-pa.announceReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.AnnounceRes{nil, fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
|
|
||||||
case req, ok := <-pa.playReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(req.Res)
|
|
||||||
|
|
||||||
case req, ok := <-pa.recordReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(req.Res)
|
|
||||||
|
|
||||||
case req, ok := <-pa.pauseReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
close(req.Res)
|
|
||||||
|
|
||||||
case req, ok := <-pa.removeReq:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, ok := pa.readPublishers[req.Author]; !ok {
|
|
||||||
close(req.Res)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
pa.readPublishersWg.Done()
|
|
||||||
close(req.Res)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pa *Path) hasExternalSource() bool {
|
func (pa *Path) hasExternalSource() bool {
|
||||||
|
|
@ -446,7 +369,6 @@ func (pa *Path) hasReadPublishersNotSources() bool {
|
||||||
|
|
||||||
func (pa *Path) addReadPublisher(c readpublisher.ReadPublisher, state readPublisherState) {
|
func (pa *Path) addReadPublisher(c readpublisher.ReadPublisher, state readPublisherState) {
|
||||||
pa.readPublishers[c] = state
|
pa.readPublishers[c] = state
|
||||||
pa.readPublishersWg.Add(1)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pa *Path) removeReadPublisher(c readpublisher.ReadPublisher) {
|
func (pa *Path) removeReadPublisher(c readpublisher.ReadPublisher) {
|
||||||
|
|
@ -781,47 +703,83 @@ func (pa *Path) Name() string {
|
||||||
|
|
||||||
// OnExtSourceSetReady is called by an external source.
|
// OnExtSourceSetReady is called by an external source.
|
||||||
func (pa *Path) OnExtSourceSetReady(req source.ExtSetReadyReq) {
|
func (pa *Path) OnExtSourceSetReady(req source.ExtSetReadyReq) {
|
||||||
pa.extSourceSetReady <- req
|
select {
|
||||||
|
case pa.extSourceSetReady <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnExtSourceSetNotReady is called by an external source.
|
// OnExtSourceSetNotReady is called by an external source.
|
||||||
func (pa *Path) OnExtSourceSetNotReady(req source.ExtSetNotReadyReq) {
|
func (pa *Path) OnExtSourceSetNotReady(req source.ExtSetNotReadyReq) {
|
||||||
pa.extSourceSetNotReady <- req
|
select {
|
||||||
|
case pa.extSourceSetNotReady <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPathManDescribe is called by pathman.PathMan.
|
// OnPathManDescribe is called by pathman.PathMan.
|
||||||
func (pa *Path) OnPathManDescribe(req readpublisher.DescribeReq) {
|
func (pa *Path) OnPathManDescribe(req readpublisher.DescribeReq) {
|
||||||
pa.describeReq <- req
|
select {
|
||||||
|
case pa.describeReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
req.Res <- readpublisher.DescribeRes{nil, "", fmt.Errorf("terminated")} //nolint:govet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPathManSetupPlay is called by pathman.PathMan.
|
// OnPathManSetupPlay is called by pathman.PathMan.
|
||||||
func (pa *Path) OnPathManSetupPlay(req readpublisher.SetupPlayReq) {
|
func (pa *Path) OnPathManSetupPlay(req readpublisher.SetupPlayReq) {
|
||||||
pa.setupPlayReq <- req
|
select {
|
||||||
|
case pa.setupPlayReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
req.Res <- readpublisher.SetupPlayRes{nil, nil, fmt.Errorf("terminated")} //nolint:govet
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPathManAnnounce is called by pathman.PathMan.
|
// OnPathManAnnounce is called by pathman.PathMan.
|
||||||
func (pa *Path) OnPathManAnnounce(req readpublisher.AnnounceReq) {
|
func (pa *Path) OnPathManAnnounce(req readpublisher.AnnounceReq) {
|
||||||
pa.announceReq <- req
|
select {
|
||||||
}
|
case pa.announceReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
// OnReadPublisherRemove is called by a readpublisher.
|
req.Res <- readpublisher.AnnounceRes{nil, fmt.Errorf("terminated")} //nolint:govet
|
||||||
func (pa *Path) OnReadPublisherRemove(req readpublisher.RemoveReq) {
|
}
|
||||||
pa.removeReq <- req
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherPlay is called by a readpublisher.
|
// OnReadPublisherPlay is called by a readpublisher.
|
||||||
func (pa *Path) OnReadPublisherPlay(req readpublisher.PlayReq) {
|
func (pa *Path) OnReadPublisherPlay(req readpublisher.PlayReq) {
|
||||||
pa.playReq <- req
|
select {
|
||||||
|
case pa.playReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherRecord is called by a readpublisher.
|
// OnReadPublisherRecord is called by a readpublisher.
|
||||||
func (pa *Path) OnReadPublisherRecord(req readpublisher.RecordReq) {
|
func (pa *Path) OnReadPublisherRecord(req readpublisher.RecordReq) {
|
||||||
pa.recordReq <- req
|
select {
|
||||||
|
case pa.recordReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherPause is called by a readpublisher.
|
// OnReadPublisherPause is called by a readpublisher.
|
||||||
func (pa *Path) OnReadPublisherPause(req readpublisher.PauseReq) {
|
func (pa *Path) OnReadPublisherPause(req readpublisher.PauseReq) {
|
||||||
pa.pauseReq <- req
|
select {
|
||||||
|
case pa.pauseReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnReadPublisherRemove is called by a readpublisher.
|
||||||
|
func (pa *Path) OnReadPublisherRemove(req readpublisher.RemoveReq) {
|
||||||
|
select {
|
||||||
|
case pa.removeReq <- req:
|
||||||
|
case <-pa.ctx.Done():
|
||||||
|
close(req.Res)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSPFrame is called by streamproc.StreamProc.
|
// OnSPFrame is called by streamproc.StreamProc.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package pathman
|
package pathman
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
@ -50,8 +51,10 @@ type PathManager struct {
|
||||||
stats *stats.Stats
|
stats *stats.Stats
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
paths map[string]*path.Path
|
ctx context.Context
|
||||||
wg sync.WaitGroup
|
ctxCancel func()
|
||||||
|
wg sync.WaitGroup
|
||||||
|
paths map[string]*path.Path
|
||||||
|
|
||||||
// in
|
// in
|
||||||
confReload chan map[string]*conf.PathConf
|
confReload chan map[string]*conf.PathConf
|
||||||
|
|
@ -59,10 +62,6 @@ type PathManager struct {
|
||||||
rpDescribe chan readpublisher.DescribeReq
|
rpDescribe chan readpublisher.DescribeReq
|
||||||
rpSetupPlay chan readpublisher.SetupPlayReq
|
rpSetupPlay chan readpublisher.SetupPlayReq
|
||||||
rpAnnounce chan readpublisher.AnnounceReq
|
rpAnnounce chan readpublisher.AnnounceReq
|
||||||
terminate chan struct{}
|
|
||||||
|
|
||||||
// out
|
|
||||||
done chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a PathManager.
|
// New allocates a PathManager.
|
||||||
|
|
@ -77,6 +76,8 @@ func New(
|
||||||
stats *stats.Stats,
|
stats *stats.Stats,
|
||||||
parent Parent) *PathManager {
|
parent Parent) *PathManager {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
pm := &PathManager{
|
pm := &PathManager{
|
||||||
rtspAddress: rtspAddress,
|
rtspAddress: rtspAddress,
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
|
|
@ -87,26 +88,28 @@ func New(
|
||||||
pathConfs: pathConfs,
|
pathConfs: pathConfs,
|
||||||
stats: stats,
|
stats: stats,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
paths: make(map[string]*path.Path),
|
paths: make(map[string]*path.Path),
|
||||||
confReload: make(chan map[string]*conf.PathConf),
|
confReload: make(chan map[string]*conf.PathConf),
|
||||||
pathClose: make(chan *path.Path),
|
pathClose: make(chan *path.Path),
|
||||||
rpDescribe: make(chan readpublisher.DescribeReq),
|
rpDescribe: make(chan readpublisher.DescribeReq),
|
||||||
rpSetupPlay: make(chan readpublisher.SetupPlayReq),
|
rpSetupPlay: make(chan readpublisher.SetupPlayReq),
|
||||||
rpAnnounce: make(chan readpublisher.AnnounceReq),
|
rpAnnounce: make(chan readpublisher.AnnounceReq),
|
||||||
terminate: make(chan struct{}),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pm.createPaths()
|
pm.createPaths()
|
||||||
|
|
||||||
|
pm.wg.Add(1)
|
||||||
go pm.run()
|
go pm.run()
|
||||||
|
|
||||||
return pm
|
return pm
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a PathManager.
|
// Close closes a PathManager.
|
||||||
func (pm *PathManager) Close() {
|
func (pm *PathManager) Close() {
|
||||||
close(pm.terminate)
|
pm.ctxCancel()
|
||||||
<-pm.done
|
pm.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log is the main logging function.
|
// Log is the main logging function.
|
||||||
|
|
@ -115,7 +118,7 @@ func (pm *PathManager) Log(level logger.Level, format string, args ...interface{
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PathManager) run() {
|
func (pm *PathManager) run() {
|
||||||
defer close(pm.done)
|
defer pm.wg.Done()
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
|
|
@ -155,7 +158,7 @@ outer:
|
||||||
pm.createPaths()
|
pm.createPaths()
|
||||||
|
|
||||||
case pa := <-pm.pathClose:
|
case pa := <-pm.pathClose:
|
||||||
if _, ok := pm.paths[pa.Name()]; !ok {
|
if pmpa, ok := pm.paths[pa.Name()]; !ok || pmpa != pa {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
delete(pm.paths, pa.Name())
|
delete(pm.paths, pa.Name())
|
||||||
|
|
@ -242,55 +245,16 @@ outer:
|
||||||
|
|
||||||
pm.paths[req.PathName].OnPathManAnnounce(req)
|
pm.paths[req.PathName].OnPathManAnnounce(req)
|
||||||
|
|
||||||
case <-pm.terminate:
|
case <-pm.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
pm.ctxCancel()
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case _, ok := <-pm.confReload:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case _, ok := <-pm.pathClose:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case req, ok := <-pm.rpDescribe:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.DescribeRes{nil, "", fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
|
|
||||||
case req, ok := <-pm.rpSetupPlay:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.SetupPlayRes{nil, nil, fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
|
|
||||||
case req, ok := <-pm.rpAnnounce:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
req.Res <- readpublisher.AnnounceRes{nil, fmt.Errorf("terminated")} //nolint:govet
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
for _, pa := range pm.paths {
|
for _, pa := range pm.paths {
|
||||||
pa.Close()
|
pa.Close()
|
||||||
}
|
}
|
||||||
pm.wg.Wait()
|
|
||||||
|
|
||||||
close(pm.confReload)
|
|
||||||
close(pm.pathClose)
|
|
||||||
close(pm.rpDescribe)
|
|
||||||
close(pm.rpSetupPlay)
|
|
||||||
close(pm.rpAnnounce)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PathManager) createPath(confName string, conf *conf.PathConf, name string) {
|
func (pm *PathManager) createPath(confName string, conf *conf.PathConf, name string) {
|
||||||
|
|
@ -339,27 +303,56 @@ func (pm *PathManager) findPathConf(name string) (string, *conf.PathConf, error)
|
||||||
|
|
||||||
// OnProgramConfReload is called by program.
|
// OnProgramConfReload is called by program.
|
||||||
func (pm *PathManager) OnProgramConfReload(pathConfs map[string]*conf.PathConf) {
|
func (pm *PathManager) OnProgramConfReload(pathConfs map[string]*conf.PathConf) {
|
||||||
pm.confReload <- pathConfs
|
select {
|
||||||
|
case pm.confReload <- pathConfs:
|
||||||
|
case <-pm.ctx.Done():
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPathClose is called by path.Path.
|
// OnPathClose is called by path.Path.
|
||||||
func (pm *PathManager) OnPathClose(pa *path.Path) {
|
func (pm *PathManager) OnPathClose(pa *path.Path) {
|
||||||
pm.pathClose <- pa
|
select {
|
||||||
|
case pm.pathClose <- pa:
|
||||||
|
case <-pm.ctx.Done():
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherDescribe is called by a ReadPublisher.
|
// OnReadPublisherDescribe is called by a ReadPublisher.
|
||||||
func (pm *PathManager) OnReadPublisherDescribe(req readpublisher.DescribeReq) {
|
func (pm *PathManager) OnReadPublisherDescribe(req readpublisher.DescribeReq) {
|
||||||
pm.rpDescribe <- req
|
select {
|
||||||
|
case pm.rpDescribe <- req:
|
||||||
|
case <-pm.ctx.Done():
|
||||||
|
req.Res <- readpublisher.DescribeRes{
|
||||||
|
SDP: nil,
|
||||||
|
Redirect: "",
|
||||||
|
Err: fmt.Errorf("terminated"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherAnnounce is called by a ReadPublisher.
|
// OnReadPublisherAnnounce is called by a ReadPublisher.
|
||||||
func (pm *PathManager) OnReadPublisherAnnounce(req readpublisher.AnnounceReq) {
|
func (pm *PathManager) OnReadPublisherAnnounce(req readpublisher.AnnounceReq) {
|
||||||
pm.rpAnnounce <- req
|
select {
|
||||||
|
case pm.rpAnnounce <- req:
|
||||||
|
case <-pm.ctx.Done():
|
||||||
|
req.Res <- readpublisher.AnnounceRes{
|
||||||
|
Path: nil,
|
||||||
|
Err: fmt.Errorf("terminated"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnReadPublisherSetupPlay is called by a ReadPublisher.
|
// OnReadPublisherSetupPlay is called by a ReadPublisher.
|
||||||
func (pm *PathManager) OnReadPublisherSetupPlay(req readpublisher.SetupPlayReq) {
|
func (pm *PathManager) OnReadPublisherSetupPlay(req readpublisher.SetupPlayReq) {
|
||||||
pm.rpSetupPlay <- req
|
select {
|
||||||
|
case pm.rpSetupPlay <- req:
|
||||||
|
case <-pm.ctx.Done():
|
||||||
|
req.Res <- readpublisher.SetupPlayRes{
|
||||||
|
Path: nil,
|
||||||
|
Tracks: nil,
|
||||||
|
Err: fmt.Errorf("terminated"),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PathManager) authenticate(
|
func (pm *PathManager) authenticate(
|
||||||
|
|
|
||||||
|
|
@ -75,12 +75,10 @@ type Conn struct {
|
||||||
pathMan PathMan
|
pathMan PathMan
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel func()
|
||||||
path readpublisher.Path
|
path readpublisher.Path
|
||||||
ringBuffer *ringbuffer.RingBuffer // read
|
ringBuffer *ringbuffer.RingBuffer // read
|
||||||
|
|
||||||
// in
|
|
||||||
terminate chan struct{}
|
|
||||||
parentTerminate chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Conn.
|
// New allocates a Conn.
|
||||||
|
|
@ -97,6 +95,8 @@ func New(
|
||||||
pathMan PathMan,
|
pathMan PathMan,
|
||||||
parent Parent) *Conn {
|
parent Parent) *Conn {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
c := &Conn{
|
c := &Conn{
|
||||||
rtspAddress: rtspAddress,
|
rtspAddress: rtspAddress,
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
|
|
@ -109,8 +109,8 @@ func New(
|
||||||
conn: rtmp.NewServerConn(nconn),
|
conn: rtmp.NewServerConn(nconn),
|
||||||
pathMan: pathMan,
|
pathMan: pathMan,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
terminate: make(chan struct{}, 1),
|
ctx: ctx,
|
||||||
parentTerminate: make(chan struct{}),
|
ctxCancel: ctxCancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
c.log(logger.Info, "opened")
|
c.log(logger.Info, "opened")
|
||||||
|
|
@ -124,15 +124,11 @@ func New(
|
||||||
// ParentClose closes a Conn.
|
// ParentClose closes a Conn.
|
||||||
func (c *Conn) ParentClose() {
|
func (c *Conn) ParentClose() {
|
||||||
c.log(logger.Info, "closed")
|
c.log(logger.Info, "closed")
|
||||||
close(c.parentTerminate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes a Conn.
|
// Close closes a Conn.
|
||||||
func (c *Conn) Close() {
|
func (c *Conn) Close() {
|
||||||
select {
|
c.ctxCancel()
|
||||||
case c.terminate <- struct{}{}:
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsReadPublisher implements readpublisher.ReadPublisher.
|
// IsReadPublisher implements readpublisher.ReadPublisher.
|
||||||
|
|
@ -175,11 +171,13 @@ func (c *Conn) run() {
|
||||||
c.log(logger.Info, "ERR: %s", err)
|
c.log(logger.Info, "ERR: %s", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-c.terminate:
|
case <-c.ctx.Done():
|
||||||
cancel()
|
cancel()
|
||||||
<-runErr
|
<-runErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.ctxCancel()
|
||||||
|
|
||||||
if c.path != nil {
|
if c.path != nil {
|
||||||
res := make(chan struct{})
|
res := make(chan struct{})
|
||||||
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
c.path.OnReadPublisherRemove(readpublisher.RemoveReq{c, res}) //nolint:govet
|
||||||
|
|
@ -187,7 +185,6 @@ func (c *Conn) run() {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.parent.OnConnClose(c)
|
c.parent.OnConnClose(c)
|
||||||
<-c.parentTerminate
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Conn) runInner(ctx context.Context) error {
|
func (c *Conn) runInner(ctx context.Context) error {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package rtmpserver
|
package rtmpserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -28,16 +29,14 @@ type Server struct {
|
||||||
pathMan *pathman.PathManager
|
pathMan *pathman.PathManager
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
l net.Listener
|
ctx context.Context
|
||||||
wg sync.WaitGroup
|
ctxCancel func()
|
||||||
conns map[*rtmpconn.Conn]struct{}
|
wg sync.WaitGroup
|
||||||
|
l net.Listener
|
||||||
|
conns map[*rtmpconn.Conn]struct{}
|
||||||
|
|
||||||
// in
|
// in
|
||||||
connClose chan *rtmpconn.Conn
|
connClose chan *rtmpconn.Conn
|
||||||
terminate chan struct{}
|
|
||||||
|
|
||||||
// out
|
|
||||||
done chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Server.
|
// New allocates a Server.
|
||||||
|
|
@ -58,6 +57,8 @@ func New(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
writeTimeout: writeTimeout,
|
writeTimeout: writeTimeout,
|
||||||
|
|
@ -68,15 +69,16 @@ func New(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
pathMan: pathMan,
|
pathMan: pathMan,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
l: l,
|
l: l,
|
||||||
conns: make(map[*rtmpconn.Conn]struct{}),
|
conns: make(map[*rtmpconn.Conn]struct{}),
|
||||||
connClose: make(chan *rtmpconn.Conn),
|
connClose: make(chan *rtmpconn.Conn),
|
||||||
terminate: make(chan struct{}),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Log(logger.Info, "listener opened on %s", address)
|
s.Log(logger.Info, "listener opened on %s", address)
|
||||||
|
|
||||||
|
s.wg.Add(1)
|
||||||
go s.run()
|
go s.run()
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
@ -89,28 +91,37 @@ func (s *Server) Log(level logger.Level, format string, args ...interface{}) {
|
||||||
|
|
||||||
// Close closes a Server.
|
// Close closes a Server.
|
||||||
func (s *Server) Close() {
|
func (s *Server) Close() {
|
||||||
close(s.terminate)
|
s.ctxCancel()
|
||||||
<-s.done
|
s.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) run() {
|
func (s *Server) run() {
|
||||||
defer close(s.done)
|
defer s.wg.Done()
|
||||||
|
|
||||||
s.wg.Add(1)
|
s.wg.Add(1)
|
||||||
connNew := make(chan net.Conn)
|
connNew := make(chan net.Conn)
|
||||||
acceptErr := make(chan error)
|
acceptErr := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer s.wg.Done()
|
defer s.wg.Done()
|
||||||
acceptErr <- func() error {
|
err := func() error {
|
||||||
for {
|
for {
|
||||||
conn, err := s.l.Accept()
|
conn, err := s.l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
connNew <- conn
|
select {
|
||||||
|
case connNew <- conn:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
conn.Close()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case acceptErr <- err:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
|
|
@ -141,44 +152,18 @@ outer:
|
||||||
}
|
}
|
||||||
s.doConnClose(c)
|
s.doConnClose(c)
|
||||||
|
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
s.ctxCancel()
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case _, ok := <-acceptErr:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
case conn, ok := <-connNew:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
conn.Close()
|
|
||||||
|
|
||||||
case _, ok := <-s.connClose:
|
|
||||||
if !ok {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
s.l.Close()
|
s.l.Close()
|
||||||
|
|
||||||
for c := range s.conns {
|
for c := range s.conns {
|
||||||
s.doConnClose(c)
|
s.doConnClose(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
s.wg.Wait()
|
|
||||||
|
|
||||||
close(acceptErr)
|
|
||||||
close(connNew)
|
|
||||||
close(s.connClose)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) doConnClose(c *rtmpconn.Conn) {
|
func (s *Server) doConnClose(c *rtmpconn.Conn) {
|
||||||
|
|
@ -189,5 +174,8 @@ func (s *Server) doConnClose(c *rtmpconn.Conn) {
|
||||||
|
|
||||||
// OnConnClose is called by rtmpconn.Conn.
|
// OnConnClose is called by rtmpconn.Conn.
|
||||||
func (s *Server) OnConnClose(c *rtmpconn.Conn) {
|
func (s *Server) OnConnClose(c *rtmpconn.Conn) {
|
||||||
s.connClose <- c
|
select {
|
||||||
|
case s.connClose <- c:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,8 +40,8 @@ type Source struct {
|
||||||
stats *stats.Stats
|
stats *stats.Stats
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
// in
|
ctx context.Context
|
||||||
terminate chan struct{}
|
ctxCancel func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Source.
|
// New allocates a Source.
|
||||||
|
|
@ -51,6 +51,9 @@ func New(ur string,
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
stats *stats.Stats,
|
stats *stats.Stats,
|
||||||
parent Parent) *Source {
|
parent Parent) *Source {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
s := &Source{
|
s := &Source{
|
||||||
ur: ur,
|
ur: ur,
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
|
|
@ -58,7 +61,8 @@ func New(ur string,
|
||||||
wg: wg,
|
wg: wg,
|
||||||
stats: stats,
|
stats: stats,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
terminate: make(chan struct{}),
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(s.stats.CountSourcesRTMP, +1)
|
atomic.AddInt64(s.stats.CountSourcesRTMP, +1)
|
||||||
|
|
@ -73,7 +77,7 @@ func New(ur string,
|
||||||
func (s *Source) Close() {
|
func (s *Source) Close() {
|
||||||
atomic.AddInt64(s.stats.CountSourcesRTMPRunning, -1)
|
atomic.AddInt64(s.stats.CountSourcesRTMPRunning, -1)
|
||||||
s.log(logger.Info, "stopped")
|
s.log(logger.Info, "stopped")
|
||||||
close(s.terminate)
|
s.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSource implements source.Source.
|
// IsSource implements source.Source.
|
||||||
|
|
@ -99,7 +103,7 @@ func (s *Source) run() {
|
||||||
select {
|
select {
|
||||||
case <-time.After(retryPause):
|
case <-time.After(retryPause):
|
||||||
return true
|
return true
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -107,6 +111,8 @@ func (s *Source) run() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) runInner() bool {
|
func (s *Source) runInner() bool {
|
||||||
|
|
@ -266,7 +272,7 @@ func (s *Source) runInner() bool {
|
||||||
s.log(logger.Info, "ERR: %s", err)
|
s.log(logger.Info, "ERR: %s", err)
|
||||||
return true
|
return true
|
||||||
|
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
cancel()
|
cancel()
|
||||||
<-runErr
|
<-runErr
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package rtspserver
|
package rtspserver
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
@ -59,16 +60,13 @@ type Server struct {
|
||||||
pathMan *pathman.PathManager
|
pathMan *pathman.PathManager
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
srv *gortsplib.Server
|
ctx context.Context
|
||||||
mutex sync.RWMutex
|
ctxCancel func()
|
||||||
conns map[*gortsplib.ServerConn]*rtspconn.Conn
|
wg sync.WaitGroup
|
||||||
sessions map[*gortsplib.ServerSession]*rtspsession.Session
|
srv *gortsplib.Server
|
||||||
|
mutex sync.RWMutex
|
||||||
// in
|
conns map[*gortsplib.ServerConn]*rtspconn.Conn
|
||||||
terminate chan struct{}
|
sessions map[*gortsplib.ServerSession]*rtspsession.Session
|
||||||
|
|
||||||
// out
|
|
||||||
done chan struct{}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Server.
|
// New allocates a Server.
|
||||||
|
|
@ -92,6 +90,8 @@ func New(
|
||||||
pathMan *pathman.PathManager,
|
pathMan *pathman.PathManager,
|
||||||
parent Parent) (*Server, error) {
|
parent Parent) (*Server, error) {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
readTimeout: readTimeout,
|
readTimeout: readTimeout,
|
||||||
isTLS: isTLS,
|
isTLS: isTLS,
|
||||||
|
|
@ -100,10 +100,10 @@ func New(
|
||||||
stats: stats,
|
stats: stats,
|
||||||
pathMan: pathMan,
|
pathMan: pathMan,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
conns: make(map[*gortsplib.ServerConn]*rtspconn.Conn),
|
conns: make(map[*gortsplib.ServerConn]*rtspconn.Conn),
|
||||||
sessions: make(map[*gortsplib.ServerSession]*rtspsession.Session),
|
sessions: make(map[*gortsplib.ServerSession]*rtspsession.Session),
|
||||||
terminate: make(chan struct{}),
|
|
||||||
done: make(chan struct{}),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.srv = &gortsplib.Server{
|
s.srv = &gortsplib.Server{
|
||||||
|
|
@ -143,6 +143,7 @@ func New(
|
||||||
|
|
||||||
s.Log(logger.Info, "TCP listener opened on %s", address)
|
s.Log(logger.Info, "TCP listener opened on %s", address)
|
||||||
|
|
||||||
|
s.wg.Add(1)
|
||||||
go s.run()
|
go s.run()
|
||||||
|
|
||||||
return s, nil
|
return s, nil
|
||||||
|
|
@ -161,18 +162,24 @@ func (s *Server) Log(level logger.Level, format string, args ...interface{}) {
|
||||||
|
|
||||||
// Close closes a Server.
|
// Close closes a Server.
|
||||||
func (s *Server) Close() {
|
func (s *Server) Close() {
|
||||||
close(s.terminate)
|
s.ctxCancel()
|
||||||
<-s.done
|
s.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) run() {
|
func (s *Server) run() {
|
||||||
defer close(s.done)
|
defer s.wg.Done()
|
||||||
|
|
||||||
serverDone := make(chan struct{})
|
s.wg.Add(1)
|
||||||
serverErr := make(chan error)
|
serverErr := make(chan error)
|
||||||
go func() {
|
go func() {
|
||||||
defer close(serverDone)
|
defer s.wg.Done()
|
||||||
serverErr <- s.srv.Wait()
|
|
||||||
|
err := s.srv.Wait()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case serverErr <- err:
|
||||||
|
case <-s.ctx.Done():
|
||||||
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
|
|
@ -181,20 +188,13 @@ outer:
|
||||||
s.Log(logger.Warn, "ERR: %s", err)
|
s.Log(logger.Warn, "ERR: %s", err)
|
||||||
break outer
|
break outer
|
||||||
|
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
s.ctxCancel()
|
||||||
for range serverErr {
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
s.srv.Close()
|
s.srv.Close()
|
||||||
|
|
||||||
<-serverDone
|
|
||||||
|
|
||||||
close(serverErr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnConnOpen implements gortsplib.ServerHandlerOnConnOpen.
|
// OnConnOpen implements gortsplib.ServerHandlerOnConnOpen.
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@ type Source struct {
|
||||||
stats *stats.Stats
|
stats *stats.Stats
|
||||||
parent Parent
|
parent Parent
|
||||||
|
|
||||||
// in
|
ctx context.Context
|
||||||
terminate chan struct{}
|
ctxCancel func()
|
||||||
}
|
}
|
||||||
|
|
||||||
// New allocates a Source.
|
// New allocates a Source.
|
||||||
|
|
@ -59,6 +59,9 @@ func New(
|
||||||
wg *sync.WaitGroup,
|
wg *sync.WaitGroup,
|
||||||
stats *stats.Stats,
|
stats *stats.Stats,
|
||||||
parent Parent) *Source {
|
parent Parent) *Source {
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
s := &Source{
|
s := &Source{
|
||||||
ur: ur,
|
ur: ur,
|
||||||
proto: proto,
|
proto: proto,
|
||||||
|
|
@ -70,7 +73,8 @@ func New(
|
||||||
wg: wg,
|
wg: wg,
|
||||||
stats: stats,
|
stats: stats,
|
||||||
parent: parent,
|
parent: parent,
|
||||||
terminate: make(chan struct{}),
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
}
|
}
|
||||||
|
|
||||||
atomic.AddInt64(s.stats.CountSourcesRTSP, +1)
|
atomic.AddInt64(s.stats.CountSourcesRTSP, +1)
|
||||||
|
|
@ -85,7 +89,7 @@ func New(
|
||||||
func (s *Source) Close() {
|
func (s *Source) Close() {
|
||||||
atomic.AddInt64(s.stats.CountSourcesRTSP, -1)
|
atomic.AddInt64(s.stats.CountSourcesRTSP, -1)
|
||||||
s.log(logger.Info, "stopped")
|
s.log(logger.Info, "stopped")
|
||||||
close(s.terminate)
|
s.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsSource implements source.Source.
|
// IsSource implements source.Source.
|
||||||
|
|
@ -111,7 +115,7 @@ func (s *Source) run() {
|
||||||
select {
|
select {
|
||||||
case <-time.After(retryPause):
|
case <-time.After(retryPause):
|
||||||
return true
|
return true
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -119,6 +123,8 @@ func (s *Source) run() {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.ctxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Source) runInner() bool {
|
func (s *Source) runInner() bool {
|
||||||
|
|
@ -154,24 +160,24 @@ func (s *Source) runInner() bool {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, ctxCancel := context.WithCancel(context.Background())
|
innerCtx, innerCtxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
var conn *gortsplib.ClientConn
|
var conn *gortsplib.ClientConn
|
||||||
var err error
|
var err error
|
||||||
dialDone := make(chan struct{})
|
dialDone := make(chan struct{})
|
||||||
go func() {
|
go func() {
|
||||||
defer close(dialDone)
|
defer close(dialDone)
|
||||||
conn, err = client.DialReadContext(ctx, s.ur)
|
conn, err = client.DialReadContext(innerCtx, s.ur)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
ctxCancel()
|
innerCtxCancel()
|
||||||
<-dialDone
|
<-dialDone
|
||||||
return false
|
return false
|
||||||
|
|
||||||
case <-dialDone:
|
case <-dialDone:
|
||||||
ctxCancel()
|
innerCtxCancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -204,7 +210,7 @@ func (s *Source) runInner() bool {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-s.terminate:
|
case <-s.ctx.Done():
|
||||||
conn.Close()
|
conn.Close()
|
||||||
<-readErr
|
<-readErr
|
||||||
return false
|
return false
|
||||||
|
|
|
||||||
18
main.go
18
main.go
|
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -25,6 +26,8 @@ import (
|
||||||
var version = "v0.0.0"
|
var version = "v0.0.0"
|
||||||
|
|
||||||
type program struct {
|
type program struct {
|
||||||
|
ctx context.Context
|
||||||
|
ctxCancel func()
|
||||||
confPath string
|
confPath string
|
||||||
conf *conf.Conf
|
conf *conf.Conf
|
||||||
confFound bool
|
confFound bool
|
||||||
|
|
@ -39,8 +42,8 @@ type program struct {
|
||||||
serverHLS *hlsserver.Server
|
serverHLS *hlsserver.Server
|
||||||
confWatcher *confwatcher.ConfWatcher
|
confWatcher *confwatcher.ConfWatcher
|
||||||
|
|
||||||
terminate chan struct{}
|
// out
|
||||||
done chan struct{}
|
done chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func newProgram(args []string) (*program, bool) {
|
func newProgram(args []string) (*program, bool) {
|
||||||
|
|
@ -62,9 +65,12 @@ func newProgram(args []string) (*program, bool) {
|
||||||
// do not check for errors
|
// do not check for errors
|
||||||
rlimit.Raise()
|
rlimit.Raise()
|
||||||
|
|
||||||
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
p := &program{
|
p := &program{
|
||||||
|
ctx: ctx,
|
||||||
|
ctxCancel: ctxCancel,
|
||||||
confPath: *argConfPath,
|
confPath: *argConfPath,
|
||||||
terminate: make(chan struct{}),
|
|
||||||
done: make(chan struct{}),
|
done: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,7 +103,7 @@ func newProgram(args []string) (*program, bool) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) close() {
|
func (p *program) close() {
|
||||||
close(p.terminate)
|
p.ctxCancel()
|
||||||
<-p.done
|
<-p.done
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -129,11 +135,13 @@ outer:
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
|
|
||||||
case <-p.terminate:
|
case <-p.ctx.Done():
|
||||||
break outer
|
break outer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
p.ctxCancel()
|
||||||
|
|
||||||
p.closeResources(nil)
|
p.closeResources(nil)
|
||||||
|
|
||||||
if p.confWatcher != nil {
|
if p.confWatcher != nil {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue