| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127 |
- package watch
- import (
- "path/filepath"
- "sync"
- "time"
- "github.com/windmilleng/fsevents"
- )
- type darwinNotify struct {
- stream *fsevents.EventStream
- events chan FileEvent
- 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
- }
- 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
- }
- func (d *darwinNotify) loop() {
- ignoredSpuriousEvent := false
- 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)
- // 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 d.isTrackingPath(e.Path) && !ignoredSpuriousEvent {
- ignoredSpuriousEvent = true
- continue
- }
- }
- d.events <- FileEvent{
- Path: e.Path,
- }
- }
- }
- }
- }
- 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 len(es.Paths) == 1 {
- go d.loop()
- es.Start()
- } 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 {
- 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),
- errors: make(chan error),
- stop: make(chan struct{}),
- }
- return dw, nil
- }
- var _ Notify = &darwinNotify{}
|