From ff7bc41d4007f67e5456703c34342df4e0113f64 Mon Sep 17 00:00:00 2001 From: Tom Payne Date: Tue, 21 Mar 2017 13:55:22 +0100 Subject: [PATCH] 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 --- inotify.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) 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 } -- 2.50.1