OSDN Git Service

new repo
[bytom/vapor.git] / vendor / github.com / rjeczalik / notify / tree_recursive.go
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, events ...Event) error {
157         if c == nil {
158                 panic("notify: Watch using nil channel")
159         }
160         // Expanding with empty event set is a nop.
161         if len(events) == 0 {
162                 return nil
163         }
164         path, isrec, err := cleanpath(path)
165         if err != nil {
166                 return err
167         }
168         eventset := joinevents(events)
169         if isrec {
170                 eventset |= recursive
171         }
172         t.rw.Lock()
173         defer t.rw.Unlock()
174         // case 1: cur is a child
175         //
176         // Look for parent watch which already covers the given path.
177         parent := node{}
178         self := false
179         err = t.root.WalkPath(path, func(nd node, isbase bool) error {
180                 if watchTotal(nd) != 0 {
181                         parent = nd
182                         self = isbase
183                         return errSkip
184                 }
185                 return nil
186         })
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)
192                 var diff eventDiff
193                 if self {
194                         diff = watchAdd(cur, c, eventset)
195                 } else {
196                         diff = watchAddInactive(parent, c, eventset)
197                 }
198                 switch {
199                 case diff == none:
200                         // the parent watchpoint already covers requested subtree with its
201                         // eventset
202                 case diff[0] == 0:
203                         // TODO(rjeczalik): cleanup this panic after implementation is stable
204                         panic("dangling watchpoint: " + parent.Name)
205                 default:
206                         if isrec || watchIsRecursive(parent) {
207                                 err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1])
208                         } else {
209                                 err = t.w.Rewatch(parent.Name, diff[0], diff[1])
210                         }
211                         if err != nil {
212                                 watchDel(parent, c, diff.Event())
213                                 return err
214                         }
215                         watchAdd(cur, c, eventset)
216                         // TODO(rjeczalik): account top-most path for c
217                         return nil
218                 }
219                 if !self {
220                         watchAdd(cur, c, eventset)
221                 }
222                 return nil
223         }
224         // case 2: cur is new parent
225         //
226         // Look for children nodes, unwatch n-1 of them and rewatch the last one.
227         var children []node
228         fn := func(nd node) error {
229                 if len(nd.Watch) == 0 {
230                         return nil
231                 }
232                 children = append(children, nd)
233                 return errSkip
234         }
235         switch must(cur.Walk(fn)); len(children) {
236         case 0:
237                 // no child watches, cur holds a new watch
238         case 1:
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]),
242                         watchTotal(cur))
243                 if err != nil {
244                         // Clean inactive watchpoint. The c chan did not exist before.
245                         cur.Child[""] = node{}
246                         delete(cur.Watch, c)
247                         return err
248                 }
249                 return nil
250         default:
251                 watchAdd(cur, c, eventset)
252                 // Copy children inactive watchpoints to the new parent.
253                 for _, nd := range children {
254                         watchCopy(nd, cur)
255                 }
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{}
260                         delete(cur.Watch, c)
261                         return err
262                 }
263                 // Unwatch children subtrees.
264                 var e error
265                 for _, nd := range children {
266                         if watchIsRecursive(nd) {
267                                 e = t.w.RecursiveUnwatch(nd.Name)
268                         } else {
269                                 e = t.w.Unwatch(nd.Name)
270                         }
271                         if e != nil {
272                                 err = nonil(err, e)
273                                 // TODO(rjeczalik): child is still watched, warn all its watchpoints
274                                 // about possible duplicate events via Error event
275                         }
276                 }
277                 return err
278         }
279         // case 3: cur is new, alone node
280         switch diff := watchAdd(cur, c, eventset); {
281         case diff == none:
282                 // TODO(rjeczalik): cleanup this panic after implementation is stable
283                 panic("watch requested but no parent watchpoint found: " + cur.Name)
284         case diff[0] == 0:
285                 if isrec {
286                         err = t.w.RecursiveWatch(cur.Name, diff[1])
287                 } else {
288                         err = t.w.Watch(cur.Name, diff[1])
289                 }
290                 if err != nil {
291                         watchDel(cur, c, diff.Event())
292                         return err
293                 }
294         default:
295                 // TODO(rjeczalik): cleanup this panic after implementation is stable
296                 panic("watch requested but no parent watchpoint found: " + cur.Name)
297         }
298         return nil
299 }
300
301 // Stop TODO(rjeczalik)
302 //
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) {
307         var err error
308         fn := func(nd node) (e error) {
309                 diff := watchDel(nd, c, all)
310                 switch {
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.
314                         return nil
315                 case diff == none:
316                         // Removing c from nd does not require shrinking its eventset.
317                 case diff[1] == 0:
318                         if watchIsRecursive(nd) {
319                                 e = t.w.RecursiveUnwatch(nd.Name)
320                         } else {
321                                 e = t.w.Unwatch(nd.Name)
322                         }
323                 default:
324                         if watchIsRecursive(nd) {
325                                 e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1])
326                         } else {
327                                 e = t.w.Rewatch(nd.Name, diff[0], diff[1])
328                         }
329                 }
330                 fn := func(nd node) error {
331                         watchDel(nd, c, all)
332                         return nil
333                 }
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
337                 // vie Error event?
338                 return errSkip
339         }
340         t.rw.Lock()
341         e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c
342         t.rw.Unlock()
343         if e != nil {
344                 err = nonil(err, e)
345         }
346         dbgprintf("Stop(%p) error: %v\n", c, err)
347 }
348
349 // Close TODO(rjeczalik)
350 func (t *recursiveTree) Close() error {
351         err := t.w.Close()
352         close(t.c)
353         return err
354 }