github.com/Hellyna/notify@v0.0.0-20210101060149-8ebdd4ef22cf/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,
   157  	_ DoNotWatchFn, events ...Event) error {
   158  	if c == nil {
   159  		panic("notify: Watch using nil channel")
   160  	}
   161  	// Expanding with empty event set is a nop.
   162  	if len(events) == 0 {
   163  		return nil
   164  	}
   165  	path, isrec, err := cleanpath(path)
   166  	if err != nil {
   167  		return err
   168  	}
   169  	eventset := joinevents(events)
   170  	if isrec {
   171  		eventset |= recursive
   172  	}
   173  	t.rw.Lock()
   174  	defer t.rw.Unlock()
   175  	// case 1: cur is a child
   176  	//
   177  	// Look for parent watch which already covers the given path.
   178  	parent := node{}
   179  	self := false
   180  	err = t.root.WalkPath(path, func(nd node, isbase bool) error {
   181  		if watchTotal(nd) != 0 {
   182  			parent = nd
   183  			self = isbase
   184  			return errSkip
   185  		}
   186  		return nil
   187  	})
   188  	cur := t.root.Add(path) // add after the walk, so it's less to traverse
   189  	if err == nil && parent.Watch != nil {
   190  		// Parent watch found. Register inactive watchpoint, so we have enough
   191  		// information to shrink the eventset on eventual Stop.
   192  		// return t.resetwatchpoint(parent, parent, c, eventset|inactive)
   193  		var diff eventDiff
   194  		if self {
   195  			diff = watchAdd(cur, c, eventset)
   196  		} else {
   197  			diff = watchAddInactive(parent, c, eventset)
   198  		}
   199  		switch {
   200  		case diff == none:
   201  			// the parent watchpoint already covers requested subtree with its
   202  			// eventset
   203  		case diff[0] == 0:
   204  			// TODO(rjeczalik): cleanup this panic after implementation is stable
   205  			panic("dangling watchpoint: " + parent.Name)
   206  		default:
   207  			if isrec || watchIsRecursive(parent) {
   208  				err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1])
   209  			} else {
   210  				err = t.w.Rewatch(parent.Name, diff[0], diff[1])
   211  			}
   212  			if err != nil {
   213  				watchDel(parent, c, diff.Event())
   214  				return err
   215  			}
   216  			watchAdd(cur, c, eventset)
   217  			// TODO(rjeczalik): account top-most path for c
   218  			return nil
   219  		}
   220  		if !self {
   221  			watchAdd(cur, c, eventset)
   222  		}
   223  		return nil
   224  	}
   225  	// case 2: cur is new parent
   226  	//
   227  	// Look for children nodes, unwatch n-1 of them and rewatch the last one.
   228  	var children []node
   229  	fn := func(nd node) error {
   230  		if len(nd.Watch) == 0 {
   231  			return nil
   232  		}
   233  		children = append(children, nd)
   234  		return errSkip
   235  	}
   236  	switch must(cur.Walk(fn, nil)); len(children) {
   237  	case 0:
   238  		// no child watches, cur holds a new watch
   239  	case 1:
   240  		watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root?
   241  		watchCopy(children[0], cur)
   242  		err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]),
   243  			watchTotal(cur))
   244  		if err != nil {
   245  			// Clean inactive watchpoint. The c chan did not exist before.
   246  			cur.Child[""] = node{}
   247  			delete(cur.Watch, c)
   248  			return err
   249  		}
   250  		return nil
   251  	default:
   252  		watchAdd(cur, c, eventset)
   253  		// Copy children inactive watchpoints to the new parent.
   254  		for _, nd := range children {
   255  			watchCopy(nd, cur)
   256  		}
   257  		// Watch parent subtree.
   258  		if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil {
   259  			// Clean inactive watchpoint. The c chan did not exist before.
   260  			cur.Child[""] = node{}
   261  			delete(cur.Watch, c)
   262  			return err
   263  		}
   264  		// Unwatch children subtrees.
   265  		var e error
   266  		for _, nd := range children {
   267  			if watchIsRecursive(nd) {
   268  				e = t.w.RecursiveUnwatch(nd.Name)
   269  			} else {
   270  				e = t.w.Unwatch(nd.Name)
   271  			}
   272  			if e != nil {
   273  				err = nonil(err, e)
   274  				// TODO(rjeczalik): child is still watched, warn all its watchpoints
   275  				// about possible duplicate events via Error event
   276  			}
   277  		}
   278  		return err
   279  	}
   280  	// case 3: cur is new, alone node
   281  	switch diff := watchAdd(cur, c, eventset); {
   282  	case diff == none:
   283  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   284  		panic("watch requested but no parent watchpoint found: " + cur.Name)
   285  	case diff[0] == 0:
   286  		if isrec {
   287  			err = t.w.RecursiveWatch(cur.Name, diff[1])
   288  		} else {
   289  			err = t.w.Watch(cur.Name, diff[1])
   290  		}
   291  		if err != nil {
   292  			watchDel(cur, c, diff.Event())
   293  			return err
   294  		}
   295  	default:
   296  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   297  		panic("watch requested but no parent watchpoint found: " + cur.Name)
   298  	}
   299  	return nil
   300  }
   301  
   302  // Stop TODO(rjeczalik)
   303  //
   304  // TODO(rjeczalik): Split parent watchpoint - transfer watches to children
   305  // if parent is no longer needed. This carries a risk that underlying
   306  // watcher calls could fail - reconsider if it's worth the effort.
   307  func (t *recursiveTree) Stop(c chan<- EventInfo) {
   308  	var err error
   309  	fn := func(nd node) (e error) {
   310  		diff := watchDel(nd, c, all)
   311  		switch {
   312  		case diff == none && watchTotal(nd) == 0:
   313  			// TODO(rjeczalik): There's no watchpoints deeper in the tree,
   314  			// probably we should remove the nodes as well.
   315  			return nil
   316  		case diff == none:
   317  			// Removing c from nd does not require shrinking its eventset.
   318  		case diff[1] == 0:
   319  			if watchIsRecursive(nd) {
   320  				e = t.w.RecursiveUnwatch(nd.Name)
   321  			} else {
   322  				e = t.w.Unwatch(nd.Name)
   323  			}
   324  		default:
   325  			if watchIsRecursive(nd) {
   326  				e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1])
   327  			} else {
   328  				e = t.w.Rewatch(nd.Name, diff[0], diff[1])
   329  			}
   330  		}
   331  		fn := func(nd node) error {
   332  			watchDel(nd, c, all)
   333  			return nil
   334  		}
   335  		err = nonil(err, e, nd.Walk(fn, nil))
   336  		// TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to
   337  		// retry un/rewatching next time and/or let the user handle the failure
   338  		// vie Error event?
   339  		return errSkip
   340  	}
   341  	t.rw.Lock()
   342  	e := t.root.Walk("", fn, nil) // TODO(rjeczalik): use max root per c
   343  	t.rw.Unlock()
   344  	if e != nil {
   345  		err = nonil(err, e)
   346  	}
   347  	dbgprintf("Stop(%p) error: %v\n", c, err)
   348  }
   349  
   350  // Close TODO(rjeczalik)
   351  func (t *recursiveTree) Close() error {
   352  	err := t.w.Close()
   353  	close(t.c)
   354  	return err
   355  }