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