compose/pkg/watch/watcher_darwin.go

133 lines
2.6 KiB
Go
Raw Normal View History

2018-08-16 20:53:47 +02:00
package watch
import (
"path/filepath"
"sync"
"time"
"github.com/windmilleng/fsevents"
)
type darwinNotify struct {
stream *fsevents.EventStream
events chan FileEvent
2018-08-16 20:53:47 +02:00
errors chan error
stop chan struct{}
// TODO(nick): This mutex is needed for the case where we add paths after we
// start watching. But because fsevents supports recursive watches, we don't
// actually need this feature. We should change the api contract of wmNotify
// so that, for recursive watches, we can guarantee that the path list doesn't
// change.
sm *sync.Mutex
// When a watch is created for a directory, we've seen fsevents non-determistically
// fire 0-3 CREATE events for that directory. We want to ignore these.
ignoreCreatedEvents map[string]bool
2018-08-16 20:53:47 +02:00
}
func (d *darwinNotify) loop() {
for {
select {
case <-d.stop:
return
case events, ok := <-d.stream.Events:
if !ok {
return
}
for _, e := range events {
e.Path = filepath.Join("/", e.Path)
if e.Flags&fsevents.ItemCreated == fsevents.ItemCreated {
d.sm.Lock()
shouldIgnore := d.ignoreCreatedEvents[e.Path]
if !shouldIgnore {
// 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 {
2018-08-16 20:53:47 +02:00
continue
}
}
d.events <- FileEvent{
Path: e.Path,
2018-08-16 20:53:47 +02:00
}
}
}
}
}
func (d *darwinNotify) Add(name string) error {
d.sm.Lock()
defer d.sm.Unlock()
es := d.stream
// Check if this is a subdirectory of any of the paths
// we're already watching.
for _, parent := range es.Paths {
isChild := pathIsChildOf(name, parent)
if isChild {
return nil
}
}
es.Paths = append(es.Paths, name)
if d.ignoreCreatedEvents == nil {
d.ignoreCreatedEvents = make(map[string]bool, 1)
}
d.ignoreCreatedEvents[name] = true
2018-08-16 20:53:47 +02:00
if len(es.Paths) == 1 {
es.Start()
go d.loop()
2018-08-16 20:53:47 +02:00
} else {
es.Restart()
}
return nil
}
func (d *darwinNotify) Close() error {
d.sm.Lock()
defer d.sm.Unlock()
d.stream.Stop()
close(d.errors)
close(d.stop)
return nil
}
func (d *darwinNotify) Events() chan FileEvent {
2018-08-16 20:53:47 +02:00
return d.events
}
func (d *darwinNotify) Errors() chan error {
return d.errors
}
func NewWatcher() (Notify, error) {
dw := &darwinNotify{
stream: &fsevents.EventStream{
Latency: 1 * time.Millisecond,
Flags: fsevents.FileEvents,
},
sm: &sync.Mutex{},
events: make(chan FileEvent),
2018-08-16 20:53:47 +02:00
errors: make(chan error),
stop: make(chan struct{}),
}
return dw, nil
}
var _ Notify = &darwinNotify{}