github.com/dgallion1/notify@v0.9.3-0.20201128171805-931189d936e0/tree_recursive.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  // watchAdd TODO(rjeczalik)
    10  func watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff {
    11  	diff := nd.Watch.Add(c, e)
    12  	if wp := nd.Child[""].Watch; len(wp) != 0 {
    13  		e = wp.Total()
    14  		diff[0] |= e
    15  		diff[1] |= e
    16  		if diff[0] == diff[1] {
    17  			return none
    18  		}
    19  	}
    20  	return diff
    21  }
    22  
    23  // watchAddInactive TODO(rjeczalik)
    24  func watchAddInactive(nd node, c chan<- EventInfo, e Event) eventDiff {
    25  	wp := nd.Child[""].Watch
    26  	if wp == nil {
    27  		wp = make(watchpoint)
    28  		nd.Child[""] = node{Watch: wp}
    29  	}
    30  	diff := wp.Add(c, e)
    31  	e = nd.Watch.Total()
    32  	diff[0] |= e
    33  	diff[1] |= e
    34  	if diff[0] == diff[1] {
    35  		return none
    36  	}
    37  	return diff
    38  }
    39  
    40  // watchCopy TODO(rjeczalik)
    41  func watchCopy(src, dst node) {
    42  	for c, e := range src.Watch {
    43  		if c == nil {
    44  			continue
    45  		}
    46  		watchAddInactive(dst, c, e)
    47  	}
    48  	if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 {
    49  		wpdst := dst.Child[""].Watch
    50  		for c, e := range wpsrc {
    51  			if c == nil {
    52  				continue
    53  			}
    54  			wpdst.Add(c, e)
    55  		}
    56  	}
    57  }
    58  
    59  // watchDel TODO(rjeczalik)
    60  func watchDel(nd node, c chan<- EventInfo, e Event) eventDiff {
    61  	diff := nd.Watch.Del(c, e)
    62  	if wp := nd.Child[""].Watch; len(wp) != 0 {
    63  		diffInactive := wp.Del(c, e)
    64  		e = wp.Total()
    65  		// TODO(rjeczalik): add e if e != all?
    66  		diff[0] |= diffInactive[0] | e
    67  		diff[1] |= diffInactive[1] | e
    68  		if diff[0] == diff[1] {
    69  			return none
    70  		}
    71  	}
    72  	return diff
    73  }
    74  
    75  // watchTotal TODO(rjeczalik)
    76  func watchTotal(nd node) Event {
    77  	e := nd.Watch.Total()
    78  	if wp := nd.Child[""].Watch; len(wp) != 0 {
    79  		e |= wp.Total()
    80  	}
    81  	return e
    82  }
    83  
    84  // watchIsRecursive TODO(rjeczalik)
    85  func watchIsRecursive(nd node) bool {
    86  	ok := nd.Watch.IsRecursive()
    87  	// TODO(rjeczalik): add a test for len(wp) != 0 change the condition.
    88  	if wp := nd.Child[""].Watch; len(wp) != 0 {
    89  		// If a watchpoint holds inactive watchpoints, it means it's a parent
    90  		// one, which is recursive by nature even though it may be not recursive
    91  		// itself.
    92  		ok = true
    93  	}
    94  	return ok
    95  }
    96  
    97  // recursiveTree TODO(rjeczalik)
    98  type recursiveTree struct {
    99  	rw   sync.RWMutex // protects root
   100  	root root
   101  	// TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #6
   102  	w interface {
   103  		watcher
   104  		recursiveWatcher
   105  	}
   106  	c chan EventInfo
   107  }
   108  
   109  // newRecursiveTree TODO(rjeczalik)
   110  func newRecursiveTree(w recursiveWatcher, c chan EventInfo) *recursiveTree {
   111  	t := &recursiveTree{
   112  		root: root{nd: newnode("")},
   113  		w: struct {
   114  			watcher
   115  			recursiveWatcher
   116  		}{w.(watcher), w},
   117  		c: c,
   118  	}
   119  	go t.dispatch()
   120  	return t
   121  }
   122  
   123  // dispatch TODO(rjeczalik)
   124  func (t *recursiveTree) dispatch() {
   125  	for ei := range t.c {
   126  		dbgprintf("dispatching %v on %q", ei.Event(), ei.Path())
   127  		go func(ei EventInfo) {
   128  			nd, ok := node{}, false
   129  			dir, base := split(ei.Path())
   130  			fn := func(it node, isbase bool) error {
   131  				if isbase {
   132  					nd = it
   133  				} else {
   134  					it.Watch.Dispatch(ei, recursive)
   135  				}
   136  				return nil
   137  			}
   138  			t.rw.RLock()
   139  			defer t.rw.RUnlock()
   140  			// Notify recursive watchpoints found on the path.
   141  			if err := t.root.WalkPath(dir, fn); err != nil {
   142  				dbgprint("dispatch did not reach leaf:", err)
   143  				return
   144  			}
   145  			// Notify parent watchpoint.
   146  			nd.Watch.Dispatch(ei, 0)
   147  			// If leaf watchpoint exists, notify it.
   148  			if nd, ok = nd.Child[base]; ok {
   149  				nd.Watch.Dispatch(ei, 0)
   150  			}
   151  		}(ei)
   152  	}
   153  }
   154  
   155  // Watch TODO(rjeczalik)
   156  func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error {
   157  	if c == nil {
   158  		panic("notify: Watch using nil channel")
   159  	}
   160  	// Expanding with empty event set is a nop.
   161  	if len(events) == 0 {
   162  		return nil
   163  	}
   164  	path, isrec, err := cleanpath(path)
   165  	if err != nil {
   166  		return err
   167  	}
   168  	eventset := joinevents(events)
   169  	if isrec {
   170  		eventset |= recursive
   171  	}
   172  	t.rw.Lock()
   173  	defer t.rw.Unlock()
   174  	// case 1: cur is a child
   175  	//
   176  	// Look for parent watch which already covers the given path.
   177  	parent := node{}
   178  	self := false
   179  	err = t.root.WalkPath(path, func(nd node, isbase bool) error {
   180  		if watchTotal(nd) != 0 {
   181  			parent = nd
   182  			self = isbase
   183  			return errSkip
   184  		}
   185  		return nil
   186  	})
   187  	cur := t.root.Add(path) // add after the walk, so it's less to traverse
   188  	if err == nil && parent.Watch != nil {
   189  		// Parent watch found. Register inactive watchpoint, so we have enough
   190  		// information to shrink the eventset on eventual Stop.
   191  		// return t.resetwatchpoint(parent, parent, c, eventset|inactive)
   192  		var diff eventDiff
   193  		if self {
   194  			diff = watchAdd(cur, c, eventset)
   195  		} else {
   196  			diff = watchAddInactive(parent, c, eventset)
   197  		}
   198  		switch {
   199  		case diff == none:
   200  			// the parent watchpoint already covers requested subtree with its
   201  			// eventset
   202  		case diff[0] == 0:
   203  			// TODO(rjeczalik): cleanup this panic after implementation is stable
   204  			panic("dangling watchpoint: " + parent.Name)
   205  		default:
   206  			if isrec || watchIsRecursive(parent) {
   207  				err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1])
   208  			} else {
   209  				err = t.w.Rewatch(parent.Name, diff[0], diff[1])
   210  			}
   211  			if err != nil {
   212  				watchDel(parent, c, diff.Event())
   213  				return err
   214  			}
   215  			watchAdd(cur, c, eventset)
   216  			// TODO(rjeczalik): account top-most path for c
   217  			return nil
   218  		}
   219  		if !self {
   220  			watchAdd(cur, c, eventset)
   221  		}
   222  		return nil
   223  	}
   224  	// case 2: cur is new parent
   225  	//
   226  	// Look for children nodes, unwatch n-1 of them and rewatch the last one.
   227  	var children []node
   228  	fn := func(nd node) error {
   229  		if len(nd.Watch) == 0 {
   230  			return nil
   231  		}
   232  		children = append(children, nd)
   233  		return errSkip
   234  	}
   235  	switch must(cur.Walk(fn)); len(children) {
   236  	case 0:
   237  		// no child watches, cur holds a new watch
   238  	case 1:
   239  		watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root?
   240  		watchCopy(children[0], cur)
   241  		err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]),
   242  			watchTotal(cur))
   243  		if err != nil {
   244  			// Clean inactive watchpoint. The c chan did not exist before.
   245  			cur.Child[""] = node{}
   246  			delete(cur.Watch, c)
   247  			return err
   248  		}
   249  		return nil
   250  	default:
   251  		watchAdd(cur, c, eventset)
   252  		// Copy children inactive watchpoints to the new parent.
   253  		for _, nd := range children {
   254  			watchCopy(nd, cur)
   255  		}
   256  		// Watch parent subtree.
   257  		if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil {
   258  			// Clean inactive watchpoint. The c chan did not exist before.
   259  			cur.Child[""] = node{}
   260  			delete(cur.Watch, c)
   261  			return err
   262  		}
   263  		// Unwatch children subtrees.
   264  		var e error
   265  		for _, nd := range children {
   266  			if watchIsRecursive(nd) {
   267  				e = t.w.RecursiveUnwatch(nd.Name)
   268  			} else {
   269  				e = t.w.Unwatch(nd.Name)
   270  			}
   271  			if e != nil {
   272  				err = nonil(err, e)
   273  				// TODO(rjeczalik): child is still watched, warn all its watchpoints
   274  				// about possible duplicate events via Error event
   275  			}
   276  		}
   277  		return err
   278  	}
   279  	// case 3: cur is new, alone node
   280  	switch diff := watchAdd(cur, c, eventset); {
   281  	case diff == none:
   282  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   283  		panic("watch requested but no parent watchpoint found: " + cur.Name)
   284  	case diff[0] == 0:
   285  		if isrec {
   286  			err = t.w.RecursiveWatch(cur.Name, diff[1])
   287  		} else {
   288  			err = t.w.Watch(cur.Name, diff[1])
   289  		}
   290  		if err != nil {
   291  			watchDel(cur, c, diff.Event())
   292  			return err
   293  		}
   294  	default:
   295  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   296  		panic("watch requested but no parent watchpoint found: " + cur.Name)
   297  	}
   298  	return nil
   299  }
   300  
   301  // Stop TODO(rjeczalik)
   302  //
   303  // TODO(rjeczalik): Split parent watchpoint - transfer watches to children
   304  // if parent is no longer needed. This carries a risk that underlying
   305  // watcher calls could fail - reconsider if it's worth the effort.
   306  func (t *recursiveTree) Stop(c chan<- EventInfo) {
   307  	var err error
   308  	fn := func(nd node) (e error) {
   309  		diff := watchDel(nd, c, all)
   310  		switch {
   311  		case diff == none && watchTotal(nd) == 0:
   312  			// TODO(rjeczalik): There's no watchpoints deeper in the tree,
   313  			// probably we should remove the nodes as well.
   314  			return nil
   315  		case diff == none:
   316  			// Removing c from nd does not require shrinking its eventset.
   317  		case diff[1] == 0:
   318  			if watchIsRecursive(nd) {
   319  				e = t.w.RecursiveUnwatch(nd.Name)
   320  			} else {
   321  				e = t.w.Unwatch(nd.Name)
   322  			}
   323  		default:
   324  			if watchIsRecursive(nd) {
   325  				e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1])
   326  			} else {
   327  				e = t.w.Rewatch(nd.Name, diff[0], diff[1])
   328  			}
   329  		}
   330  		fn := func(nd node) error {
   331  			watchDel(nd, c, all)
   332  			return nil
   333  		}
   334  		err = nonil(err, e, nd.Walk(fn))
   335  		// TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to
   336  		// retry un/rewatching next time and/or let the user handle the failure
   337  		// vie Error event?
   338  		return errSkip
   339  	}
   340  	t.rw.Lock()
   341  	e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c
   342  	t.rw.Unlock()
   343  	if e != nil {
   344  		err = nonil(err, e)
   345  	}
   346  	dbgprintf("Stop(%p) error: %v\n", c, err)
   347  }
   348  
   349  // Close TODO(rjeczalik)
   350  func (t *recursiveTree) Close() error {
   351  	err := t.w.Close()
   352  	close(t.c)
   353  	return err
   354  }