Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 9f483ed

Browse files
committed
rewrite watcher impl. to watch dirs
1 parent 7edee90 commit 9f483ed

File tree

1 file changed

+116
-31
lines changed

1 file changed

+116
-31
lines changed

agent/agentcontainers/watcher/watcher.go

+116-31
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package watcher
88

99
import (
1010
"context"
11+
"path/filepath"
1112
"sync"
1213

1314
"github.com/fsnotify/fsnotify"
@@ -36,32 +37,90 @@ type Watcher interface {
3637

3738
type fsnotifyWatcher struct {
3839
*fsnotify.Watcher
39-
closeOnce sync.Once
40-
closed chan struct{}
40+
closed bool
41+
done chan struct{}
42+
mu sync.Mutex // Protects following.
43+
watchedFiles map[string]bool // Files being watched (absolute path -> bool).
44+
watchedDirs map[string]int // Refcount of directories being watched (absolute path -> count).
4145
}
4246

47+
// NewFSNotify creates a new file system watcher that watches parent directories
48+
// instead of individual files for more reliable event detection.
4349
func NewFSNotify() (Watcher, error) {
4450
w, err := fsnotify.NewWatcher()
4551
if err != nil {
4652
return nil, xerrors.Errorf("create fsnotify watcher: %w", err)
4753
}
4854
return &fsnotifyWatcher{
49-
Watcher: w,
50-
closed: make(chan struct{}),
55+
Watcher: w,
56+
done: make(chan struct{}),
57+
watchedFiles: make(map[string]bool),
58+
watchedDirs: make(map[string]int),
5159
}, nil
5260
}
5361

54-
func (f *fsnotifyWatcher) Add(path string) error {
55-
if err := f.Watcher.Add(path); err != nil {
56-
return xerrors.Errorf("add path to watcher: %w", err)
62+
func (f *fsnotifyWatcher) Add(file string) error {
63+
absPath, err := filepath.Abs(file)
64+
if err != nil {
65+
return xerrors.Errorf("absolute path: %w", err)
66+
}
67+
68+
dir := filepath.Dir(absPath)
69+
70+
f.mu.Lock()
71+
defer f.mu.Unlock()
72+
73+
// Already watching this file.
74+
if f.watchedFiles[absPath] {
75+
return nil
5776
}
77+
78+
// Start watching the parent directory if not already watching.
79+
if f.watchedDirs[dir] == 0 {
80+
if err := f.Watcher.Add(dir); err != nil {
81+
return xerrors.Errorf("add directory to watcher: %w", err)
82+
}
83+
}
84+
85+
// Increment the reference count for this directory.
86+
f.watchedDirs[dir]++
87+
// Mark this file as watched.
88+
f.watchedFiles[absPath] = true
89+
5890
return nil
5991
}
6092

61-
func (f *fsnotifyWatcher) Remove(path string) error {
62-
if err := f.Watcher.Remove(path); err != nil {
63-
return xerrors.Errorf("remove path from watcher: %w", err)
93+
func (f *fsnotifyWatcher) Remove(file string) error {
94+
absPath, err := filepath.Abs(file)
95+
if err != nil {
96+
return xerrors.Errorf("absolute path: %w", err)
6497
}
98+
99+
dir := filepath.Dir(absPath)
100+
101+
f.mu.Lock()
102+
defer f.mu.Unlock()
103+
104+
// Not watching this file.
105+
if !f.watchedFiles[absPath] {
106+
return nil
107+
}
108+
109+
// Remove the file from our watch list.
110+
delete(f.watchedFiles, absPath)
111+
112+
// Decrement the reference count for this directory.
113+
f.watchedDirs[dir]--
114+
115+
// If no more files in this directory are being watched, stop
116+
// watching the directory.
117+
if f.watchedDirs[dir] <= 0 {
118+
if err := f.Watcher.Remove(dir); err != nil {
119+
return xerrors.Errorf("remove directory from watcher: %w", err)
120+
}
121+
delete(f.watchedDirs, dir)
122+
}
123+
65124
return nil
66125
}
67126

@@ -73,31 +132,57 @@ func (f *fsnotifyWatcher) Next(ctx context.Context) (event *fsnotify.Event, err
73132
}
74133
}()
75134

76-
select {
77-
case <-ctx.Done():
78-
return nil, ctx.Err()
79-
case event, ok := <-f.Events:
80-
if !ok {
81-
return nil, ErrWatcherClosed
82-
}
83-
return &event, nil
84-
case err, ok := <-f.Errors:
85-
if !ok {
135+
for {
136+
select {
137+
case <-ctx.Done():
138+
return nil, ctx.Err()
139+
case evt, ok := <-f.Events:
140+
if !ok {
141+
return nil, ErrWatcherClosed
142+
}
143+
144+
// Get the absolute path to match against our watched files.
145+
absPath, err := filepath.Abs(evt.Name)
146+
if err != nil {
147+
continue
148+
}
149+
150+
f.mu.Lock()
151+
isWatched := f.watchedFiles[absPath]
152+
f.mu.Unlock()
153+
if isWatched {
154+
return &evt, nil
155+
}
156+
157+
continue // Ignore events for files not being watched.
158+
159+
case err, ok := <-f.Errors:
160+
if !ok {
161+
return nil, ErrWatcherClosed
162+
}
163+
return nil, xerrors.Errorf("watcher error: %w", err)
164+
case <-f.done:
86165
return nil, ErrWatcherClosed
87166
}
88-
return nil, xerrors.Errorf("watcher error: %w", err)
89-
case <-f.closed:
90-
return nil, ErrWatcherClosed
91167
}
92168
}
93169

94170
func (f *fsnotifyWatcher) Close() (err error) {
95-
err = ErrWatcherClosed
96-
f.closeOnce.Do(func() {
97-
if err = f.Watcher.Close(); err != nil {
98-
err = xerrors.Errorf("close watcher: %w", err)
99-
}
100-
close(f.closed)
101-
})
102-
return err
171+
f.mu.Lock()
172+
f.watchedFiles = nil
173+
f.watchedDirs = nil
174+
closed := f.closed
175+
f.mu.Unlock()
176+
177+
if closed {
178+
return ErrWatcherClosed
179+
}
180+
181+
close(f.done)
182+
183+
if err := f.Watcher.Close(); err != nil {
184+
return xerrors.Errorf("close watcher: %w", err)
185+
}
186+
187+
return nil
103188
}

0 commit comments

Comments
 (0)