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.
9 // nonrecursiveTree TODO(rjeczalik)
10 type nonrecursiveTree struct {
11 rw sync.RWMutex // protects root
18 // newNonrecursiveTree TODO(rjeczalik)
19 func newNonrecursiveTree(w watcher, c, rec chan EventInfo) *nonrecursiveTree {
21 rec = make(chan EventInfo, buffer)
23 t := &nonrecursiveTree{
24 root: root{nd: newnode("")},
34 // dispatch TODO(rjeczalik)
35 func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) {
37 dbgprintf("dispatching %v on %q", ei.Event(), ei.Path())
38 go func(ei EventInfo) {
41 dir, base := split(ei.Path())
42 fn := func(it node, isbase bool) error {
43 isrec = isrec || it.Watch.IsRecursive()
47 it.Watch.Dispatch(ei, recursive)
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)
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)
67 // If the event describes newly leaf directory created within
68 if !isrec || ei.Event() != Create {
71 if ok, err := ei.(isDirer).isDir(); !ok || err != nil {
79 // internal TODO(rjeczalik)
80 func (t *nonrecursiveTree) internal(rec <-chan EventInfo) {
85 t.root.WalkPath(ei.Path(), func(it node, _ bool) error {
86 if e := it.Watch[t.rec]; e != 0 && e > eset {
96 err := nd.Add(ei.Path()).AddDir(t.recFunc(eset))
99 dbgprintf("internal(%p) error: %v", rec, err)
104 // watchAdd TODO(rjeczalik)
105 func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff {
106 if e&recursive != 0 {
107 diff := nd.Watch.Add(t.rec, e|Create|omit)
111 return nd.Watch.Add(c, e)
114 // watchDelMin TODO(rjeczalik)
115 func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff {
116 old, ok := nd.Watch[t.rec]
118 nd.Watch[t.rec] = min
120 diff := nd.Watch.Del(c, e)
122 switch old &^= diff[0] &^ diff[1]; {
123 case old|internal == internal:
124 delete(nd.Watch, t.rec)
125 if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 {
126 delete(nd.Watch, nil)
129 nd.Watch.Add(t.rec, old|Create)
132 case diff[1]|Create == diff[0]:
142 // watchDel TODO(rjeczalik)
143 func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff {
144 return t.watchDelMin(0, nd, c, e)
147 // Watch TODO(rjeczalik)
148 func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error {
150 panic("notify: Watch using nil channel")
152 // Expanding with empty event set is a nop.
153 if len(events) == 0 {
156 path, isrec, err := cleanpath(path)
160 eset := joinevents(events)
163 nd := t.root.Add(path)
165 return t.watchrec(nd, c, eset|recursive)
167 return t.watch(nd, c, eset)
170 func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) {
171 diff := nd.Watch.Add(c, e)
176 // TODO(rjeczalik): cleanup this panic after implementation is stable
177 panic("eset is empty: " + nd.Name)
179 err = t.w.Watch(nd.Name, diff[1])
181 err = t.w.Rewatch(nd.Name, diff[0], diff[1])
184 nd.Watch.Del(c, diff.Event())
190 func (t *nonrecursiveTree) recFunc(e Event) walkFunc {
191 return func(nd node) error {
192 switch diff := nd.Watch.Add(t.rec, e|omit|Create); {
195 // TODO(rjeczalik): cleanup this panic after implementation is stable
196 panic("eset is empty: " + nd.Name)
198 t.w.Watch(nd.Name, diff[1])
200 t.w.Rewatch(nd.Name, diff[0], diff[1])
206 func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event) error {
207 var traverse func(walkFunc) error
208 // Non-recursive tree listens on Create event for every recursive
209 // watchpoint in order to automagically set a watch for every
210 // created directory.
211 switch diff := nd.Watch.dryAdd(t.rec, e|Create); {
214 nd.Watch.Add(t.rec, e|omit|Create)
217 // TODO(rjeczalik): cleanup this panic after implementation is stable
218 panic("eset is empty: " + nd.Name)
220 // TODO(rjeczalik): BFS into directories and skip subtree as soon as first
221 // recursive watchpoint is encountered.
226 // TODO(rjeczalik): account every path that failed to be (re)watched
228 if err := traverse(t.recFunc(e)); err != nil {
235 type walkWatchpointFunc func(Event, node) error
237 func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error {
242 mnd := minode{nd: nd}
243 stack := []minode{mnd}
245 for n := len(stack); n != 0; n = len(stack) {
246 mnd, stack = stack[n-1], stack[:n-1]
247 // There must be no recursive watchpoints if the node has no watchpoints
248 // itself (every node in subtree rooted at recursive watchpoints must
249 // have at least nil (total) and t.rec watchpoints).
250 if len(mnd.nd.Watch) != 0 {
251 switch err := fn(mnd.min, mnd.nd); err {
259 for _, nd := range mnd.nd.Child {
260 stack = append(stack, minode{mnd.nd.Watch[t.rec], nd})
266 // Stop TODO(rjeczalik)
267 func (t *nonrecursiveTree) Stop(c chan<- EventInfo) {
268 fn := func(min Event, nd node) error {
269 // TODO(rjeczalik): aggregate watcher errors and retry; in worst case
270 // forward to the user.
271 switch diff := t.watchDelMin(min, nd, c, all); {
277 t.w.Rewatch(nd.Name, diff[0], diff[1])
282 err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c
284 dbgprintf("Stop(%p) error: %v\n", c, err)
287 // Close TODO(rjeczalik)
288 func (t *nonrecursiveTree) Close() error {