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