diff --git a/pkg/imagemanager/distillfs/daemon.go b/pkg/imagemanager/distillfs/daemon.go index 91bf049..ec31d56 100644 --- a/pkg/imagemanager/distillfs/daemon.go +++ b/pkg/imagemanager/distillfs/daemon.go @@ -397,16 +397,18 @@ func (d *Daemon) shouldRemount() bool { } func (d *Daemon) startWatch() { - d.watcherActive.Store(true) - go d.watch() + if !d.watcherActive.CompareAndSwap(false, true) { + return + } + go d.watch(d.stopChan, d.kickStop) } -func (d *Daemon) watch() { +func (d *Daemon) watch(stopChan chan struct{}, kickStop *Stopper) { ticker := time.NewTicker(5 * time.Second) defer func() { ticker.Stop() logrus.WithFields(d.daemonLogFields()).Info("daemon exited") - close(d.stopChan) + close(stopChan) // Set watcherActive to false before remount // remount will call startWatch() which sets it back to true d.watcherActive.Store(false) @@ -421,7 +423,7 @@ func (d *Daemon) watch() { if !d.tick() { return } - case <-d.kickStop.Done(): + case <-kickStop.Done(): for d.IsAlive() { time.Sleep(10 * time.Millisecond) } diff --git a/pkg/imagemanager/distillfs/daemon_test.go b/pkg/imagemanager/distillfs/daemon_test.go index 6406d21..a754891 100644 --- a/pkg/imagemanager/distillfs/daemon_test.go +++ b/pkg/imagemanager/distillfs/daemon_test.go @@ -508,6 +508,34 @@ func newMockDaemon(tmpDir string) *mockDaemon { return mock } +func TestDaemon_StartWatchOnlyOnce(t *testing.T) { + tmpDir := t.TempDir() + mock := newMockDaemon(tmpDir) + mock.stopChan = make(chan struct{}) + mock.kickStop = NewStopper() + mock.mockIsAlive = func() bool { return false } + mock.setState(DaemonStateUnmounting) + + for i := 0; i < 10; i++ { + mock.startWatch() + } + mock.kickStop.Close() + + select { + case <-mock.stopChan: + case <-time.After(time.Second): + t.Fatal("watcher did not stop") + } + + deadline := time.Now().Add(time.Second) + for mock.watcherActive.Load() && time.Now().Before(deadline) { + time.Sleep(time.Millisecond) + } + if mock.watcherActive.Load() { + t.Fatal("watcher remained active after stopping") + } +} + func TestDaemonApplyConfigProtectsCredentials(t *testing.T) { mock := newMockDaemon(t.TempDir()) mock.config = &BackendConfig{