mirror of
https://github.com/bluenviron/mediamtx.git
synced 2025-12-20 02:00:05 -08:00
fix hot reloading of configuration on macOS and with text editors that deletes and recreates the configuration file
This commit is contained in:
parent
e0845a0af8
commit
0f11dd5c71
2 changed files with 36 additions and 22 deletions
|
|
@ -2,11 +2,17 @@ package confwatcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fsnotify/fsnotify"
|
"github.com/fsnotify/fsnotify"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
minInterval = 1 * time.Second
|
||||||
|
additionalWait = 10 * time.Millisecond
|
||||||
|
)
|
||||||
|
|
||||||
// ConfWatcher is a configuration file watcher.
|
// ConfWatcher is a configuration file watcher.
|
||||||
type ConfWatcher struct {
|
type ConfWatcher struct {
|
||||||
inner *fsnotify.Watcher
|
inner *fsnotify.Watcher
|
||||||
|
|
@ -24,7 +30,10 @@ func New(confPath string) (*ConfWatcher, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := os.Stat(confPath); err == nil {
|
if _, err := os.Stat(confPath); err == nil {
|
||||||
err := inner.Add(confPath)
|
// use absolute path to support Darwin
|
||||||
|
absolutePath, _ := filepath.Abs(confPath)
|
||||||
|
|
||||||
|
err := inner.Add(absolutePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
inner.Close()
|
inner.Close()
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -54,13 +63,22 @@ func (w *ConfWatcher) Close() {
|
||||||
func (w *ConfWatcher) run() {
|
func (w *ConfWatcher) run() {
|
||||||
defer close(w.done)
|
defer close(w.done)
|
||||||
|
|
||||||
|
var lastCalled time.Time
|
||||||
|
|
||||||
outer:
|
outer:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case event := <-w.inner.Events:
|
case event := <-w.inner.Events:
|
||||||
if (event.Op & fsnotify.Write) == fsnotify.Write {
|
if time.Since(lastCalled) < minInterval {
|
||||||
// wait some additional time to avoid EOF
|
continue
|
||||||
time.Sleep(10 * time.Millisecond)
|
}
|
||||||
|
|
||||||
|
if (event.Op&fsnotify.Write) == fsnotify.Write ||
|
||||||
|
(event.Op&fsnotify.Create) == fsnotify.Create {
|
||||||
|
// wait some additional time to allow the writer to complete its job
|
||||||
|
time.Sleep(additionalWait)
|
||||||
|
|
||||||
|
lastCalled = time.Now()
|
||||||
w.signal <- struct{}{}
|
w.signal <- struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,7 +90,7 @@ outer:
|
||||||
close(w.signal)
|
close(w.signal)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Watch returns when the configuration file has changed.
|
// Watch returns a channel that is called when the configuration file has changed.
|
||||||
func (w *ConfWatcher) Watch() chan struct{} {
|
func (w *ConfWatcher) Watch() chan struct{} {
|
||||||
return w.signal
|
return w.signal
|
||||||
}
|
}
|
||||||
|
|
|
||||||
30
main.go
30
main.go
|
|
@ -68,13 +68,20 @@ func newProgram(args []string) (*program, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = p.createResources(true)
|
err = p.createDynamicResources(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
p.closeResources()
|
p.closeAllResources()
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
p.confWatcher, err = confwatcher.New(p.confPath)
|
||||||
|
if err != nil {
|
||||||
|
p.closeAllResources()
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
go p.run()
|
go p.run()
|
||||||
|
|
||||||
return p, nil
|
return p, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,10 +117,10 @@ outer:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p.closeResources()
|
p.closeAllResources()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) createResources(initial bool) error {
|
func (p *program) createDynamicResources(initial bool) error {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if p.stats == nil {
|
if p.stats == nil {
|
||||||
|
|
@ -187,17 +194,10 @@ func (p *program) createResources(initial bool) error {
|
||||||
p.pathMan, p.serverTcp, p)
|
p.pathMan, p.serverTcp, p)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.confWatcher == nil {
|
|
||||||
p.confWatcher, err = confwatcher.New(p.confPath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *program) closeResources() {
|
func (p *program) closeAllResources() {
|
||||||
if p.confWatcher != nil {
|
if p.confWatcher != nil {
|
||||||
p.confWatcher.Close()
|
p.confWatcher.Close()
|
||||||
}
|
}
|
||||||
|
|
@ -243,10 +243,6 @@ func (p *program) reloadConf() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// always recreate confWatcher to avoid reloading twice
|
|
||||||
p.confWatcher.Close()
|
|
||||||
p.confWatcher = nil
|
|
||||||
|
|
||||||
closeLogHandler := false
|
closeLogHandler := false
|
||||||
if !reflect.DeepEqual(conf.LogDestinationsParsed, p.conf.LogDestinationsParsed) ||
|
if !reflect.DeepEqual(conf.LogDestinationsParsed, p.conf.LogDestinationsParsed) ||
|
||||||
conf.LogFile != p.conf.LogFile {
|
conf.LogFile != p.conf.LogFile {
|
||||||
|
|
@ -345,7 +341,7 @@ func (p *program) reloadConf() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
p.conf = conf
|
p.conf = conf
|
||||||
return p.createResources(false)
|
return p.createDynamicResources(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue