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