]> go.fuhry.dev Git - fsnotify.git/commitdiff
Watch.Add improvements (avoid race, fix consistency, reduce garbage) (#189)
authorTom Payne <twpayne@gmail.com>
Tue, 21 Mar 2017 12:55:22 +0000 (13:55 +0100)
committerPawel Knap <ppknap@users.noreply.github.com>
Tue, 21 Mar 2017 12:55:22 +0000 (13:55 +0100)
* 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

index bfa9dbc3c7c6528b0ceda7fa7e64a60ba8df0cb9..15a489321165ff276f3958a9b1aaa924c343449b 100644 (file)
@@ -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
 }