Skip to content

Commit 8a5d4ab

Browse files
committed
kqueue: Make watcher.Close() O(n) instead of O(n^2)
1 parent 7f4cf4d commit 8a5d4ab

File tree

1 file changed

+18
-6
lines changed

1 file changed

+18
-6
lines changed

kqueue.go

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Watcher struct {
2828

2929
mu sync.Mutex // Protects access to watcher data
3030
watches map[string]int // Map of watched file descriptors (key: path).
31+
watchesByDir map[string][]int // Map of watched file descriptors indexed by the parent directory (key: dirname(path)).
3132
externalWatches map[string]bool // Map of watches added by user of the library.
3233
dirFlags map[string]uint32 // Map of watched directories to fflags used in kqueue.
3334
paths map[int]pathInfo // Map file descriptors to path names for processing kqueue events.
@@ -50,6 +51,7 @@ func NewWatcher() (*Watcher, error) {
5051
w := &Watcher{
5152
kq: kq,
5253
watches: make(map[string]int),
54+
watchesByDir: make(map[string][]int),
5355
dirFlags: make(map[string]uint32),
5456
paths: make(map[int]pathInfo),
5557
fileExists: make(map[string]bool),
@@ -119,6 +121,16 @@ func (w *Watcher) Remove(name string) error {
119121
w.mu.Lock()
120122
isDir := w.paths[watchfd].isDir
121123
delete(w.watches, name)
124+
125+
parentName := filepath.Clean(filepath.Dir(name))
126+
parentWatches := w.watchesByDir[parentName]
127+
for i, fd := range parentWatches {
128+
if fd == watchfd {
129+
w.watchesByDir[parentName] = append(parentWatches[:i], parentWatches[i+1:]...)
130+
break
131+
}
132+
}
133+
122134
delete(w.paths, watchfd)
123135
delete(w.dirFlags, name)
124136
w.mu.Unlock()
@@ -127,12 +139,10 @@ func (w *Watcher) Remove(name string) error {
127139
if isDir {
128140
var pathsToRemove []string
129141
w.mu.Lock()
130-
for _, path := range w.paths {
131-
wdir, _ := filepath.Split(path.name)
132-
if filepath.Clean(wdir) == name {
133-
if !w.externalWatches[path.name] {
134-
pathsToRemove = append(pathsToRemove, path.name)
135-
}
142+
for _, fd := range w.watchesByDir[name] {
143+
path := w.paths[fd]
144+
if !w.externalWatches[path.name] {
145+
pathsToRemove = append(pathsToRemove, path.name)
136146
}
137147
}
138148
w.mu.Unlock()
@@ -231,7 +241,9 @@ func (w *Watcher) addWatch(name string, flags uint32) (string, error) {
231241

232242
if !alreadyWatching {
233243
w.mu.Lock()
244+
parentName := filepath.Clean(filepath.Dir(name))
234245
w.watches[name] = watchfd
246+
w.watchesByDir[parentName] = append(w.watchesByDir[parentName], watchfd)
235247
w.paths[watchfd] = pathInfo{name: name, isDir: isDir}
236248
w.mu.Unlock()
237249
}

0 commit comments

Comments
 (0)