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