From 3a86068bbda422b567908a908854f958378fe78c Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Fri, 15 May 2026 21:18:09 +0200 Subject: [PATCH 1/2] debounce and improve config file watcher --- share/settings/users.go | 34 ++++++++++++++++++++++++++++------ 1 file changed, 28 insertions(+), 6 deletions(-) diff --git a/share/settings/users.go b/share/settings/users.go index a6f0a093..05114b45 100644 --- a/share/settings/users.go +++ b/share/settings/users.go @@ -5,8 +5,12 @@ import ( "errors" "fmt" "os" + "path/filepath" "regexp" + "runtime" + "strings" "sync" + "time" "github.com/fsnotify/fsnotify" "github.com/jpillora/chisel/share/cio" @@ -102,19 +106,37 @@ func (u *UserIndex) addWatchEvents() error { if err != nil { return err } - if err := watcher.Add(u.configFile); err != nil { + var configPath, _ = filepath.Abs(u.configFile) + if err := watcher.Add(filepath.Dir(configPath)); err != nil { return err } go func() { + var debounceTimer *time.Timer + var debounceMutex sync.Mutex for e := range watcher.Events { - if e.Op&fsnotify.Write != fsnotify.Write { + eventPath, _ := filepath.Abs(e.Name) + if runtime.GOOS == "windows" { + if !strings.EqualFold(eventPath, configPath) { + continue + } + } else if eventPath != configPath { continue } - if err := u.loadUserIndex(); err != nil { - u.Infof("Failed to reload the users configuration: %s", err) - } else { - u.Debugf("Users configuration successfully reloaded from: %s", u.configFile) + if e.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 { + continue } + if debounceTimer != nil { + debounceTimer.Stop() + } + debounceTimer = time.AfterFunc(200*time.Millisecond, func() { + debounceMutex.Lock() + defer debounceMutex.Unlock() + if err := u.loadUserIndex(); err != nil { + u.Infof("Failed to reload the users configuration: %s", err) + } else { + u.Debugf("Users configuration successfully reloaded from: %s", u.configFile) + } + }) } }() return nil From 705d63119c652fb6e543bc768cab690168a65a0d Mon Sep 17 00:00:00 2001 From: Daniel Hammerschmidt Date: Sat, 16 May 2026 17:58:03 +0200 Subject: [PATCH 2/2] drain fsnotify watcher Error channel --- share/settings/users.go | 70 +++++++++++++++++++++++++++-------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/share/settings/users.go b/share/settings/users.go index 05114b45..b877247c 100644 --- a/share/settings/users.go +++ b/share/settings/users.go @@ -106,38 +106,60 @@ func (u *UserIndex) addWatchEvents() error { if err != nil { return err } - var configPath, _ = filepath.Abs(u.configFile) + configPath, err := filepath.Abs(u.configFile) + if err != nil { + return err + } if err := watcher.Add(filepath.Dir(configPath)); err != nil { return err } go func() { var debounceTimer *time.Timer var debounceMutex sync.Mutex - for e := range watcher.Events { - eventPath, _ := filepath.Abs(e.Name) - if runtime.GOOS == "windows" { - if !strings.EqualFold(eventPath, configPath) { - continue + for { + select { + case e, ok := <-watcher.Events: { + if !ok { + u.Infof("Stop watching the users configuration: fsnotify Events channel closed.") + return + } + eventPath, err := filepath.Abs(e.Name) + if err != nil { + continue + } + if runtime.GOOS == "windows" { + if !strings.EqualFold(eventPath, configPath) { + continue + } + } else if eventPath != configPath { + continue + } + if e.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 { + continue + } + if debounceTimer != nil { + debounceTimer.Stop() + } + debounceTimer = time.AfterFunc(200*time.Millisecond, func() { + debounceMutex.Lock() + defer debounceMutex.Unlock() + if err := u.loadUserIndex(); err != nil { + u.Infof("Failed to reload the users configuration: %s", err) + } else { + u.Debugf("Users configuration successfully reloaded from: %s", u.configFile) + } + }) } - } else if eventPath != configPath { - continue - } - if e.Op&(fsnotify.Write|fsnotify.Create|fsnotify.Rename) == 0 { - continue - } - if debounceTimer != nil { - debounceTimer.Stop() - } - debounceTimer = time.AfterFunc(200*time.Millisecond, func() { - debounceMutex.Lock() - defer debounceMutex.Unlock() - if err := u.loadUserIndex(); err != nil { - u.Infof("Failed to reload the users configuration: %s", err) - } else { - u.Debugf("Users configuration successfully reloaded from: %s", u.configFile) + case err, ok := <-watcher.Errors: { + if !ok { + u.Infof("Stop watching the users configuration: fsnotify Errors channel closed.") + return + } + // just log and continue running with current config + u.Infof("Error while watching the users configuration: %s", err) } - }) - } + } + } }() return nil }