watch: a new strategy for handling spurious events, hoping to fix race conditions (#163)

This commit is contained in:
Nick Santos 2018-08-23 13:24:47 -04:00 committed by Nicolas De loof
parent 4801d2b1a4
commit 4562b0bf95
2 changed files with 25 additions and 15 deletions

View File

@ -134,6 +134,9 @@ func TestWatchNonExistentPath(t *testing.T) {
if err != nil {
t.Fatal(err)
}
f.fsync()
d1 := []byte("hello\ngo\n")
err = ioutil.WriteFile(path, d1, 0644)
if err != nil {

View File

@ -20,21 +20,13 @@ type darwinNotify struct {
// so that, for recursive watches, we can guarantee that the path list doesn't
// change.
sm *sync.Mutex
}
func (d *darwinNotify) isTrackingPath(path string) bool {
d.sm.Lock()
defer d.sm.Unlock()
for _, p := range d.stream.Paths {
if p == path {
return true
}
}
return false
// ignore the first event that says the watched directory
// has been created. these are fired spuriously on initiation.
ignoreCreatedEvents map[string]bool
}
func (d *darwinNotify) loop() {
ignoredSpuriousEvents := make(map[string]bool, 0)
for {
select {
case <-d.stop:
@ -47,11 +39,20 @@ func (d *darwinNotify) loop() {
for _, e := range events {
e.Path = filepath.Join("/", e.Path)
// ignore the first event that says the watched directory
// has been created. these are fired spuriously on initiation.
if e.Flags&fsevents.ItemCreated == fsevents.ItemCreated {
if !ignoredSpuriousEvents[e.Path] && d.isTrackingPath(e.Path) {
ignoredSpuriousEvents[e.Path] = true
d.sm.Lock()
shouldIgnore := d.ignoreCreatedEvents[e.Path]
if shouldIgnore {
d.ignoreCreatedEvents[e.Path] = false
} else {
// If we got a created event for something
// that's not on the ignore list, we assume
// we're done with the spurious events.
d.ignoreCreatedEvents = nil
}
d.sm.Unlock()
if shouldIgnore {
continue
}
}
@ -80,6 +81,12 @@ func (d *darwinNotify) Add(name string) error {
}
es.Paths = append(es.Paths, name)
if d.ignoreCreatedEvents == nil {
d.ignoreCreatedEvents = make(map[string]bool, 1)
}
d.ignoreCreatedEvents[name] = true
if len(es.Paths) == 1 {
go d.loop()
es.Start()