From: Tom Payne Date: Tue, 21 Mar 2017 12:55:22 +0000 (+0100) Subject: Watch.Add improvements (avoid race, fix consistency, reduce garbage) (#189) X-Git-Tag: v1.7.2~176 X-Git-Url: https://go.fuhry.dev/?a=commitdiff_plain;h=ff7bc41d4007f67e5456703c34342df4e0113f64;p=fsnotify.git Watch.Add improvements (avoid race, fix consistency, reduce garbage) (#189) * hold mutex for longer to avoid race condition If Watcher.Add is called with the same name from multiple goroutines and name is not already in the watches map then the last call to complete wins and the watch created by the first call to complete is lost. This commit holds the mutex for the full duration of the update to the watches map. * avoid clobbering existing flags if inotify_add_watch fails Previously, the watch entry is the watches map was updated before the call to InotifyAddWatch. If InotifyAddWatch fails then this left the watch entry in an inconsistent state. * reuse existing watch entry when possible --- diff --git a/inotify.go b/inotify.go index bfa9dbc..15a4893 100644 --- a/inotify.go +++ b/inotify.go @@ -103,21 +103,23 @@ func (w *Watcher) Add(name string) error { var flags uint32 = agnosticEvents w.mu.Lock() - watchEntry, found := w.watches[name] - w.mu.Unlock() - if found { - watchEntry.flags |= flags - flags |= unix.IN_MASK_ADD + defer w.mu.Unlock() + watchEntry := w.watches[name] + if watchEntry != nil { + flags |= watchEntry.flags | unix.IN_MASK_ADD } wd, errno := unix.InotifyAddWatch(w.fd, name, flags) if wd == -1 { return errno } - w.mu.Lock() - w.watches[name] = &watch{wd: uint32(wd), flags: flags} - w.paths[wd] = name - w.mu.Unlock() + if watchEntry == nil { + w.watches[name] = &watch{wd: uint32(wd), flags: flags} + w.paths[wd] = name + } else { + watchEntry.wd = uint32(wd) + watchEntry.flags = flags + } return nil }