github.com/DiversionCompany/notify@v0.9.9/tree_nonrecursive.go (about)

     1  // Copyright (c) 2014-2015 The Notify Authors. All rights reserved.
     2  // Use of this source code is governed by the MIT license that can be
     3  // found in the LICENSE file.
     4  
     5  package notify
     6  
     7  import (
     8  	"os"
     9  	"sync"
    10  )
    11  
    12  // nonrecursiveTree TODO(rjeczalik)
    13  type nonrecursiveTree struct {
    14  	rw   sync.RWMutex // protects root
    15  	root root
    16  	w    watcher
    17  	c    chan EventInfo
    18  	rec  chan EventInfo
    19  }
    20  
    21  // newNonrecursiveTree TODO(rjeczalik)
    22  func newNonrecursiveTree(w watcher, c, rec chan EventInfo) *nonrecursiveTree {
    23  	if rec == nil {
    24  		rec = make(chan EventInfo, buffer)
    25  	}
    26  	t := &nonrecursiveTree{
    27  		root: root{nd: newnode("")},
    28  		w:    w,
    29  		c:    c,
    30  		rec:  rec,
    31  	}
    32  	go t.dispatch(c)
    33  	go t.internal(rec)
    34  	return t
    35  }
    36  
    37  // dispatch TODO(rjeczalik)
    38  func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
    39  	for ei := range c {
    40  		dbgprintf("dispatching %v on %q", ei.Event(), ei.Path())
    41  		t.dispatchEvent(ei)
    42  	}
    43  }
    44  
    45  func (t *nonrecursiveTree) dispatchEvent(ei EventInfo) {
    46  	var nd node
    47  	var isrec bool
    48  	dir, base := split(ei.Path())
    49  	fn := func(it node, isbase bool) error {
    50  		isrec = isrec || it.Watch.IsRecursive()
    51  		if isbase {
    52  			nd = it
    53  		} else {
    54  			it.Watch.Dispatch(ei, recursive)
    55  		}
    56  		return nil
    57  	}
    58  	t.rw.RLock()
    59  	// Notify recursive watchpoints found on the path.
    60  	if err := t.root.WalkPath(dir, fn); err != nil {
    61  		if !os.IsNotExist(err) {
    62  			dbgprint("dispatch did not reach leaf:", err)
    63  		}
    64  		t.rw.RUnlock()
    65  		return
    66  	}
    67  	// Notify parent watchpoint.
    68  	nd.Watch.Dispatch(ei, 0)
    69  	isrec = isrec || nd.Watch.IsRecursive()
    70  	// If leaf watchpoint exists, notify it.
    71  	if nd, ok := nd.Child[base]; ok {
    72  		isrec = isrec || nd.Watch.IsRecursive()
    73  		nd.Watch.Dispatch(ei, 0)
    74  	}
    75  	t.rw.RUnlock()
    76  	// If the event describes newly leaf directory created within
    77  	if !isrec || ei.Event()&(Create|Remove) == 0 {
    78  		return
    79  	}
    80  	if ok, err := ei.(isDirer).isDir(); !ok || err != nil {
    81  		return
    82  	}
    83  	t.rec <- ei
    84  }
    85  
    86  // internal TODO(rjeczalik)
    87  func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
    88  	for ei := range rec {
    89  		t.rw.Lock()
    90  		if ei.Event() == Remove {
    91  			nd, err := t.root.Get(ei.Path())
    92  			if err != nil {
    93  				t.rw.Unlock()
    94  				continue
    95  			}
    96  			t.walkWatchpoint(nd, func(_ Event, nd node) error {
    97  				t.w.Unwatch(nd.Name)
    98  				return nil
    99  			})
   100  			t.root.Del(ei.Path())
   101  			t.rw.Unlock()
   102  			continue
   103  		}
   104  		var nd node
   105  		var eset = internal
   106  		t.root.WalkPath(ei.Path(), func(it node, _ bool) error {
   107  			if e := it.Watch[t.rec]; e != 0 && e > eset {
   108  				eset = e
   109  			}
   110  			nd = it
   111  			return nil
   112  		})
   113  		if eset == internal {
   114  			t.rw.Unlock()
   115  			continue
   116  		}
   117  		if ei.Path() != nd.Name {
   118  			nd = nd.Add(ei.Path())
   119  		}
   120  		err := nd.AddDir(t.recFunc(eset), nil)
   121  		t.rw.Unlock()
   122  		if err != nil {
   123  			dbgprintf("internal(%p) error: %v", rec, err)
   124  		}
   125  	}
   126  }
   127  
   128  // watchAdd TODO(rjeczalik)
   129  func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff {
   130  	if e&recursive != 0 {
   131  		diff := nd.Watch.Add(t.rec, e|Create|omit)
   132  		nd.Watch.Add(c, e)
   133  		return diff
   134  	}
   135  	return nd.Watch.Add(c, e)
   136  }
   137  
   138  // watchDelMin TODO(rjeczalik)
   139  func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff {
   140  	old, ok := nd.Watch[t.rec]
   141  	if ok {
   142  		nd.Watch[t.rec] = min
   143  	}
   144  	diff := nd.Watch.Del(c, e)
   145  	if ok {
   146  		switch old &^= diff[0] &^ diff[1]; {
   147  		case old|internal == internal:
   148  			delete(nd.Watch, t.rec)
   149  			if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 {
   150  				delete(nd.Watch, nil)
   151  			}
   152  		default:
   153  			nd.Watch.Add(t.rec, old|Create)
   154  			switch {
   155  			case diff == none:
   156  			case diff[1]|Create == diff[0]:
   157  				diff = none
   158  			default:
   159  				diff[1] |= Create
   160  			}
   161  		}
   162  	}
   163  	return diff
   164  }
   165  
   166  // watchDel TODO(rjeczalik)
   167  func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff {
   168  	return t.watchDelMin(0, nd, c, e)
   169  }
   170  
   171  // Watch TODO(rjeczalik)
   172  func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo,
   173  	doNotWatch DoNotWatchFn, events ...Event) error {
   174  	if c == nil {
   175  		panic("notify: Watch using nil channel")
   176  	}
   177  	// Expanding with empty event set is a nop.
   178  	if len(events) == 0 {
   179  		return nil
   180  	}
   181  	path, isrec, err := cleanpath(path)
   182  	if err != nil {
   183  		return err
   184  	}
   185  	eset := joinevents(events)
   186  	t.rw.Lock()
   187  	defer t.rw.Unlock()
   188  	nd := t.root.Add(path)
   189  	if isrec {
   190  		return t.watchrec(nd, c, eset|recursive, doNotWatch)
   191  	}
   192  	return t.watch(nd, c, eset)
   193  }
   194  
   195  func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) {
   196  	diff := nd.Watch.Add(c, e)
   197  	switch {
   198  	case diff == none:
   199  		return nil
   200  	case diff[1] == 0:
   201  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   202  		panic("eset is empty: " + nd.Name)
   203  	case diff[0] == 0:
   204  		err = t.w.Watch(nd.Name, diff[1])
   205  	default:
   206  		err = t.w.Rewatch(nd.Name, diff[0], diff[1])
   207  	}
   208  	if err != nil {
   209  		nd.Watch.Del(c, diff.Event())
   210  		return err
   211  	}
   212  	return nil
   213  }
   214  
   215  func (t *nonrecursiveTree) recFunc(e Event) walkFunc {
   216  	return func(nd node) (err error) {
   217  		switch diff := nd.Watch.Add(t.rec, e|omit|Create); {
   218  		case diff == none:
   219  		case diff[1] == 0:
   220  			// TODO(rjeczalik): cleanup this panic after implementation is stable
   221  			panic("eset is empty: " + nd.Name)
   222  		case diff[0] == 0:
   223  			err = t.w.Watch(nd.Name, diff[1])
   224  		default:
   225  			err = t.w.Rewatch(nd.Name, diff[0], diff[1])
   226  		}
   227  		return
   228  	}
   229  }
   230  
   231  func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event,
   232  	doNotWatch DoNotWatchFn) error {
   233  	var traverse func(walkFunc, DoNotWatchFn) error
   234  	// Non-recursive tree listens on Create event for every recursive
   235  	// watchpoint in order to automagically set a watch for every
   236  	// created directory.
   237  	switch diff := nd.Watch.dryAdd(t.rec, e|Create); {
   238  	case diff == none:
   239  		t.watchAdd(nd, c, e)
   240  		nd.Watch.Add(t.rec, e|omit|Create)
   241  		return nil
   242  	case diff[1] == 0:
   243  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   244  		panic("eset is empty: " + nd.Name)
   245  	case diff[0] == 0:
   246  		// TODO(rjeczalik): BFS into directories and skip subtree as soon as first
   247  		// recursive watchpoint is encountered.
   248  		traverse = nd.AddDir
   249  	default:
   250  		traverse = nd.Walk
   251  	}
   252  	// TODO(rjeczalik): account every path that failed to be (re)watched
   253  	// and retry.
   254  	if err := traverse(t.recFunc(e), doNotWatch); err != nil {
   255  		return err
   256  	}
   257  	t.watchAdd(nd, c, e)
   258  	return nil
   259  }
   260  
   261  type walkWatchpointFunc func(Event, node) error
   262  
   263  func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error {
   264  	type minode struct {
   265  		min Event
   266  		nd  node
   267  	}
   268  	mnd := minode{nd: nd}
   269  	stack := []minode{mnd}
   270  Traverse:
   271  	for n := len(stack); n != 0; n = len(stack) {
   272  		mnd, stack = stack[n-1], stack[:n-1]
   273  		// There must be no recursive watchpoints if the node has no watchpoints
   274  		// itself (every node in subtree rooted at recursive watchpoints must
   275  		// have at least nil (total) and t.rec watchpoints).
   276  		if len(mnd.nd.Watch) != 0 {
   277  			switch err := fn(mnd.min, mnd.nd); err {
   278  			case nil:
   279  			case errSkip:
   280  				continue Traverse
   281  			default:
   282  				return err
   283  			}
   284  		}
   285  		for _, nd := range mnd.nd.Child {
   286  			stack = append(stack, minode{mnd.nd.Watch[t.rec], nd})
   287  		}
   288  	}
   289  	return nil
   290  }
   291  
   292  // Stop TODO(rjeczalik)
   293  func (t *nonrecursiveTree) Stop(c chan<- EventInfo) {
   294  	fn := func(min Event, nd node) error {
   295  		// TODO(rjeczalik): aggregate watcher errors and retry; in worst case
   296  		// forward to the user.
   297  		switch diff := t.watchDelMin(min, nd, c, all); {
   298  		case diff == none:
   299  			return nil
   300  		case diff[1] == 0:
   301  			t.w.Unwatch(nd.Name)
   302  		default:
   303  			t.w.Rewatch(nd.Name, diff[0], diff[1])
   304  		}
   305  		return nil
   306  	}
   307  	t.rw.Lock()
   308  	err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c
   309  	t.rw.Unlock()
   310  	dbgprintf("Stop(%p) error: %v\n", c, err)
   311  }
   312  
   313  // Close TODO(rjeczalik)
   314  func (t *nonrecursiveTree) Close() error {
   315  	err := t.w.Close()
   316  	close(t.c)
   317  	return err
   318  }