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 // 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 {
16 if diff[0] == diff[1] {
23 // watchAddInactive TODO(rjeczalik)
24 func watchAddInactive(nd node, c chan<- EventInfo, e Event) eventDiff {
25 wp := nd.Child[""].Watch
28 nd.Child[""] = node{Watch: wp}
34 if diff[0] == diff[1] {
40 // watchCopy TODO(rjeczalik)
41 func watchCopy(src, dst node) {
42 for c, e := range src.Watch {
46 watchAddInactive(dst, c, e)
48 if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 {
49 wpdst := dst.Child[""].Watch
50 for c, e := range wpsrc {
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)
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] {
75 // watchTotal TODO(rjeczalik)
76 func watchTotal(nd node) Event {
78 if wp := nd.Child[""].Watch; len(wp) != 0 {
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
97 // recursiveTree TODO(rjeczalik)
98 type recursiveTree struct {
99 rw sync.RWMutex // protects root
101 // TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #6
109 // newRecursiveTree TODO(rjeczalik)
110 func newRecursiveTree(w recursiveWatcher, c chan EventInfo) *recursiveTree {
112 root: root{nd: newnode("")},
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 {
134 it.Watch.Dispatch(ei, recursive)
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)
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)
155 // Watch TODO(rjeczalik)
156 func (t *recursiveTree) Watch(path string, c chan<- EventInfo, events ...Event) error {
158 panic("notify: Watch using nil channel")
160 // Expanding with empty event set is a nop.
161 if len(events) == 0 {
164 path, isrec, err := cleanpath(path)
168 eventset := joinevents(events)
170 eventset |= recursive
174 // case 1: cur is a child
176 // Look for parent watch which already covers the given path.
179 err = t.root.WalkPath(path, func(nd node, isbase bool) error {
180 if watchTotal(nd) != 0 {
187 cur := t.root.Add(path) // add after the walk, so it's less to traverse
188 if err == nil && parent.Watch != nil {
189 // Parent watch found. Register inactive watchpoint, so we have enough
190 // information to shrink the eventset on eventual Stop.
191 // return t.resetwatchpoint(parent, parent, c, eventset|inactive)
194 diff = watchAdd(cur, c, eventset)
196 diff = watchAddInactive(parent, c, eventset)
200 // the parent watchpoint already covers requested subtree with its
203 // TODO(rjeczalik): cleanup this panic after implementation is stable
204 panic("dangling watchpoint: " + parent.Name)
206 if isrec || watchIsRecursive(parent) {
207 err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1])
209 err = t.w.Rewatch(parent.Name, diff[0], diff[1])
212 watchDel(parent, c, diff.Event())
215 watchAdd(cur, c, eventset)
216 // TODO(rjeczalik): account top-most path for c
220 watchAdd(cur, c, eventset)
224 // case 2: cur is new parent
226 // Look for children nodes, unwatch n-1 of them and rewatch the last one.
228 fn := func(nd node) error {
229 if len(nd.Watch) == 0 {
232 children = append(children, nd)
235 switch must(cur.Walk(fn)); len(children) {
237 // no child watches, cur holds a new watch
239 watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root?
240 watchCopy(children[0], cur)
241 err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]),
244 // Clean inactive watchpoint. The c chan did not exist before.
245 cur.Child[""] = node{}
251 watchAdd(cur, c, eventset)
252 // Copy children inactive watchpoints to the new parent.
253 for _, nd := range children {
256 // Watch parent subtree.
257 if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil {
258 // Clean inactive watchpoint. The c chan did not exist before.
259 cur.Child[""] = node{}
263 // Unwatch children subtrees.
265 for _, nd := range children {
266 if watchIsRecursive(nd) {
267 e = t.w.RecursiveUnwatch(nd.Name)
269 e = t.w.Unwatch(nd.Name)
273 // TODO(rjeczalik): child is still watched, warn all its watchpoints
274 // about possible duplicate events via Error event
279 // case 3: cur is new, alone node
280 switch diff := watchAdd(cur, c, eventset); {
282 // TODO(rjeczalik): cleanup this panic after implementation is stable
283 panic("watch requested but no parent watchpoint found: " + cur.Name)
286 err = t.w.RecursiveWatch(cur.Name, diff[1])
288 err = t.w.Watch(cur.Name, diff[1])
291 watchDel(cur, c, diff.Event())
295 // TODO(rjeczalik): cleanup this panic after implementation is stable
296 panic("watch requested but no parent watchpoint found: " + cur.Name)
301 // Stop TODO(rjeczalik)
303 // TODO(rjeczalik): Split parent watchpoint - transfer watches to children
304 // if parent is no longer needed. This carries a risk that underlying
305 // watcher calls could fail - reconsider if it's worth the effort.
306 func (t *recursiveTree) Stop(c chan<- EventInfo) {
308 fn := func(nd node) (e error) {
309 diff := watchDel(nd, c, all)
311 case diff == none && watchTotal(nd) == 0:
312 // TODO(rjeczalik): There's no watchpoints deeper in the tree,
313 // probably we should remove the nodes as well.
316 // Removing c from nd does not require shrinking its eventset.
318 if watchIsRecursive(nd) {
319 e = t.w.RecursiveUnwatch(nd.Name)
321 e = t.w.Unwatch(nd.Name)
324 if watchIsRecursive(nd) {
325 e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1])
327 e = t.w.Rewatch(nd.Name, diff[0], diff[1])
330 fn := func(nd node) error {
334 err = nonil(err, e, nd.Walk(fn))
335 // TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to
336 // retry un/rewatching next time and/or let the user handle the failure
341 e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c
346 dbgprintf("Stop(%p) error: %v\n", c, err)
349 // Close TODO(rjeczalik)
350 func (t *recursiveTree) Close() error {