github.com/FabianKramm/notify@v0.9.3-0.20210719135015-4705c29227a1/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|Remove) == 0 {
    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  		t.rw.Lock()
    83  		if ei.Event() == Remove {
    84  			nd, err := t.root.Get(ei.Path())
    85  			if err != nil {
    86  				t.rw.Unlock()
    87  				continue
    88  			}
    89  			t.walkWatchpoint(nd, func(_ Event, nd node) error {
    90  				t.w.Unwatch(nd.Name)
    91  				return nil
    92  			})
    93  			t.root.Del(ei.Path())
    94  			t.rw.Unlock()
    95  			continue
    96  		}
    97  		var nd node
    98  		var eset = internal
    99  		t.root.WalkPath(ei.Path(), func(it node, _ bool) error {
   100  			if e := it.Watch[t.rec]; e != 0 && e > eset {
   101  				eset = e
   102  			}
   103  			nd = it
   104  			return nil
   105  		})
   106  		if eset == internal {
   107  			t.rw.Unlock()
   108  			continue
   109  		}
   110  		if ei.Path() != nd.Name {
   111  			nd = nd.Add(ei.Path())
   112  		}
   113  		err := nd.AddDir(t.recFunc(eset))
   114  		t.rw.Unlock()
   115  		if err != nil {
   116  			dbgprintf("internal(%p) error: %v", rec, err)
   117  		}
   118  	}
   119  }
   120  
   121  // watchAdd TODO(rjeczalik)
   122  func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff {
   123  	if e&recursive != 0 {
   124  		diff := nd.Watch.Add(t.rec, e|Create|omit)
   125  		nd.Watch.Add(c, e)
   126  		return diff
   127  	}
   128  	return nd.Watch.Add(c, e)
   129  }
   130  
   131  // watchDelMin TODO(rjeczalik)
   132  func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff {
   133  	old, ok := nd.Watch[t.rec]
   134  	if ok {
   135  		nd.Watch[t.rec] = min
   136  	}
   137  	diff := nd.Watch.Del(c, e)
   138  	if ok {
   139  		switch old &^= diff[0] &^ diff[1]; {
   140  		case old|internal == internal:
   141  			delete(nd.Watch, t.rec)
   142  			if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 {
   143  				delete(nd.Watch, nil)
   144  			}
   145  		default:
   146  			nd.Watch.Add(t.rec, old|Create)
   147  			switch {
   148  			case diff == none:
   149  			case diff[1]|Create == diff[0]:
   150  				diff = none
   151  			default:
   152  				diff[1] |= Create
   153  			}
   154  		}
   155  	}
   156  	return diff
   157  }
   158  
   159  // watchDel TODO(rjeczalik)
   160  func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff {
   161  	return t.watchDelMin(0, nd, c, e)
   162  }
   163  
   164  // Watch TODO(rjeczalik)
   165  func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error {
   166  	if c == nil {
   167  		panic("notify: Watch using nil channel")
   168  	}
   169  	// Expanding with empty event set is a nop.
   170  	if len(events) == 0 {
   171  		return nil
   172  	}
   173  	path, isrec, err := cleanpath(path)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	eset := joinevents(events)
   178  	t.rw.Lock()
   179  	defer t.rw.Unlock()
   180  	nd := t.root.Add(path)
   181  	if isrec {
   182  		return t.watchrec(nd, c, eset|recursive)
   183  	}
   184  	return t.watch(nd, c, eset)
   185  }
   186  
   187  func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) {
   188  	diff := nd.Watch.Add(c, e)
   189  	switch {
   190  	case diff == none:
   191  		return nil
   192  	case diff[1] == 0:
   193  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   194  		panic("eset is empty: " + nd.Name)
   195  	case diff[0] == 0:
   196  		err = t.w.Watch(nd.Name, diff[1])
   197  	default:
   198  		err = t.w.Rewatch(nd.Name, diff[0], diff[1])
   199  	}
   200  	if err != nil {
   201  		nd.Watch.Del(c, diff.Event())
   202  		return err
   203  	}
   204  	return nil
   205  }
   206  
   207  func (t *nonrecursiveTree) recFunc(e Event) walkFunc {
   208  	return func(nd node) error {
   209  		switch diff := nd.Watch.Add(t.rec, e|omit|Create); {
   210  		case diff == none:
   211  		case diff[1] == 0:
   212  			// TODO(rjeczalik): cleanup this panic after implementation is stable
   213  			panic("eset is empty: " + nd.Name)
   214  		case diff[0] == 0:
   215  			t.w.Watch(nd.Name, diff[1])
   216  		default:
   217  			t.w.Rewatch(nd.Name, diff[0], diff[1])
   218  		}
   219  		return nil
   220  	}
   221  }
   222  
   223  func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event) error {
   224  	var traverse func(walkFunc) error
   225  	// Non-recursive tree listens on Create event for every recursive
   226  	// watchpoint in order to automagically set a watch for every
   227  	// created directory.
   228  	switch diff := nd.Watch.dryAdd(t.rec, e|Create); {
   229  	case diff == none:
   230  		t.watchAdd(nd, c, e)
   231  		nd.Watch.Add(t.rec, e|omit|Create)
   232  		return nil
   233  	case diff[1] == 0:
   234  		// TODO(rjeczalik): cleanup this panic after implementation is stable
   235  		panic("eset is empty: " + nd.Name)
   236  	case diff[0] == 0:
   237  		// TODO(rjeczalik): BFS into directories and skip subtree as soon as first
   238  		// recursive watchpoint is encountered.
   239  		traverse = nd.AddDir
   240  	default:
   241  		traverse = nd.Walk
   242  	}
   243  	// TODO(rjeczalik): account every path that failed to be (re)watched
   244  	// and retry.
   245  	if err := traverse(t.recFunc(e)); err != nil {
   246  		return err
   247  	}
   248  	t.watchAdd(nd, c, e)
   249  	return nil
   250  }
   251  
   252  type walkWatchpointFunc func(Event, node) error
   253  
   254  func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error {
   255  	type minode struct {
   256  		min Event
   257  		nd  node
   258  	}
   259  	mnd := minode{nd: nd}
   260  	stack := []minode{mnd}
   261  Traverse:
   262  	for n := len(stack); n != 0; n = len(stack) {
   263  		mnd, stack = stack[n-1], stack[:n-1]
   264  		// There must be no recursive watchpoints if the node has no watchpoints
   265  		// itself (every node in subtree rooted at recursive watchpoints must
   266  		// have at least nil (total) and t.rec watchpoints).
   267  		if len(mnd.nd.Watch) != 0 {
   268  			switch err := fn(mnd.min, mnd.nd); err {
   269  			case nil:
   270  			case errSkip:
   271  				continue Traverse
   272  			default:
   273  				return err
   274  			}
   275  		}
   276  		for _, nd := range mnd.nd.Child {
   277  			stack = append(stack, minode{mnd.nd.Watch[t.rec], nd})
   278  		}
   279  	}
   280  	return nil
   281  }
   282  
   283  // Stop TODO(rjeczalik)
   284  func (t *nonrecursiveTree) Stop(c chan<- EventInfo) {
   285  	fn := func(min Event, nd node) error {
   286  		// TODO(rjeczalik): aggregate watcher errors and retry; in worst case
   287  		// forward to the user.
   288  		switch diff := t.watchDelMin(min, nd, c, all); {
   289  		case diff == none:
   290  			return nil
   291  		case diff[1] == 0:
   292  			t.w.Unwatch(nd.Name)
   293  		default:
   294  			t.w.Rewatch(nd.Name, diff[0], diff[1])
   295  		}
   296  		return nil
   297  	}
   298  	t.rw.Lock()
   299  	err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c
   300  	t.rw.Unlock()
   301  	dbgprintf("Stop(%p) error: %v\n", c, err)
   302  }
   303  
   304  // Close TODO(rjeczalik)
   305  func (t *nonrecursiveTree) Close() error {
   306  	err := t.w.Close()
   307  	close(t.c)
   308  	return err
   309  }