github.com/DiversionCompany/notify@v0.9.9/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 ( 8 "os" 9 "sync" 10 ) 11 12 // nonrecursiveTree TODO(rjeczalik) 13 type nonrecursiveTree struct { 14 rw sync.RWMutex // protects root 15 root root 16 w watcher 17 c chan EventInfo 18 rec chan EventInfo 19 } 20 21 // newNonrecursiveTree TODO(rjeczalik) 22 func newNonrecursiveTree(w watcher, c, rec chan EventInfo) *nonrecursiveTree { 23 if rec == nil { 24 rec = make(chan EventInfo, buffer) 25 } 26 t := &nonrecursiveTree{ 27 root: root{nd: newnode("")}, 28 w: w, 29 c: c, 30 rec: rec, 31 } 32 go t.dispatch(c) 33 go t.internal(rec) 34 return t 35 } 36 37 // dispatch TODO(rjeczalik) 38 func (t *nonrecursiveTree) dispatch(c <-chan EventInfo) { 39 for ei := range c { 40 dbgprintf("dispatching %v on %q", ei.Event(), ei.Path()) 41 t.dispatchEvent(ei) 42 } 43 } 44 45 func (t *nonrecursiveTree) dispatchEvent(ei EventInfo) { 46 var nd node 47 var isrec bool 48 dir, base := split(ei.Path()) 49 fn := func(it node, isbase bool) error { 50 isrec = isrec || it.Watch.IsRecursive() 51 if isbase { 52 nd = it 53 } else { 54 it.Watch.Dispatch(ei, recursive) 55 } 56 return nil 57 } 58 t.rw.RLock() 59 // Notify recursive watchpoints found on the path. 60 if err := t.root.WalkPath(dir, fn); err != nil { 61 if !os.IsNotExist(err) { 62 dbgprint("dispatch did not reach leaf:", err) 63 } 64 t.rw.RUnlock() 65 return 66 } 67 // Notify parent watchpoint. 68 nd.Watch.Dispatch(ei, 0) 69 isrec = isrec || nd.Watch.IsRecursive() 70 // If leaf watchpoint exists, notify it. 71 if nd, ok := nd.Child[base]; ok { 72 isrec = isrec || nd.Watch.IsRecursive() 73 nd.Watch.Dispatch(ei, 0) 74 } 75 t.rw.RUnlock() 76 // If the event describes newly leaf directory created within 77 if !isrec || ei.Event()&(Create|Remove) == 0 { 78 return 79 } 80 if ok, err := ei.(isDirer).isDir(); !ok || err != nil { 81 return 82 } 83 t.rec <- ei 84 } 85 86 // internal TODO(rjeczalik) 87 func (t *nonrecursiveTree) internal(rec <-chan EventInfo) { 88 for ei := range rec { 89 t.rw.Lock() 90 if ei.Event() == Remove { 91 nd, err := t.root.Get(ei.Path()) 92 if err != nil { 93 t.rw.Unlock() 94 continue 95 } 96 t.walkWatchpoint(nd, func(_ Event, nd node) error { 97 t.w.Unwatch(nd.Name) 98 return nil 99 }) 100 t.root.Del(ei.Path()) 101 t.rw.Unlock() 102 continue 103 } 104 var nd node 105 var eset = internal 106 t.root.WalkPath(ei.Path(), func(it node, _ bool) error { 107 if e := it.Watch[t.rec]; e != 0 && e > eset { 108 eset = e 109 } 110 nd = it 111 return nil 112 }) 113 if eset == internal { 114 t.rw.Unlock() 115 continue 116 } 117 if ei.Path() != nd.Name { 118 nd = nd.Add(ei.Path()) 119 } 120 err := nd.AddDir(t.recFunc(eset), nil) 121 t.rw.Unlock() 122 if err != nil { 123 dbgprintf("internal(%p) error: %v", rec, err) 124 } 125 } 126 } 127 128 // watchAdd TODO(rjeczalik) 129 func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff { 130 if e&recursive != 0 { 131 diff := nd.Watch.Add(t.rec, e|Create|omit) 132 nd.Watch.Add(c, e) 133 return diff 134 } 135 return nd.Watch.Add(c, e) 136 } 137 138 // watchDelMin TODO(rjeczalik) 139 func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff { 140 old, ok := nd.Watch[t.rec] 141 if ok { 142 nd.Watch[t.rec] = min 143 } 144 diff := nd.Watch.Del(c, e) 145 if ok { 146 switch old &^= diff[0] &^ diff[1]; { 147 case old|internal == internal: 148 delete(nd.Watch, t.rec) 149 if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 { 150 delete(nd.Watch, nil) 151 } 152 default: 153 nd.Watch.Add(t.rec, old|Create) 154 switch { 155 case diff == none: 156 case diff[1]|Create == diff[0]: 157 diff = none 158 default: 159 diff[1] |= Create 160 } 161 } 162 } 163 return diff 164 } 165 166 // watchDel TODO(rjeczalik) 167 func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff { 168 return t.watchDelMin(0, nd, c, e) 169 } 170 171 // Watch TODO(rjeczalik) 172 func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo, 173 doNotWatch DoNotWatchFn, events ...Event) error { 174 if c == nil { 175 panic("notify: Watch using nil channel") 176 } 177 // Expanding with empty event set is a nop. 178 if len(events) == 0 { 179 return nil 180 } 181 path, isrec, err := cleanpath(path) 182 if err != nil { 183 return err 184 } 185 eset := joinevents(events) 186 t.rw.Lock() 187 defer t.rw.Unlock() 188 nd := t.root.Add(path) 189 if isrec { 190 return t.watchrec(nd, c, eset|recursive, doNotWatch) 191 } 192 return t.watch(nd, c, eset) 193 } 194 195 func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) { 196 diff := nd.Watch.Add(c, e) 197 switch { 198 case diff == none: 199 return nil 200 case diff[1] == 0: 201 // TODO(rjeczalik): cleanup this panic after implementation is stable 202 panic("eset is empty: " + nd.Name) 203 case diff[0] == 0: 204 err = t.w.Watch(nd.Name, diff[1]) 205 default: 206 err = t.w.Rewatch(nd.Name, diff[0], diff[1]) 207 } 208 if err != nil { 209 nd.Watch.Del(c, diff.Event()) 210 return err 211 } 212 return nil 213 } 214 215 func (t *nonrecursiveTree) recFunc(e Event) walkFunc { 216 return func(nd node) (err error) { 217 switch diff := nd.Watch.Add(t.rec, e|omit|Create); { 218 case diff == none: 219 case diff[1] == 0: 220 // TODO(rjeczalik): cleanup this panic after implementation is stable 221 panic("eset is empty: " + nd.Name) 222 case diff[0] == 0: 223 err = t.w.Watch(nd.Name, diff[1]) 224 default: 225 err = t.w.Rewatch(nd.Name, diff[0], diff[1]) 226 } 227 return 228 } 229 } 230 231 func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event, 232 doNotWatch DoNotWatchFn) error { 233 var traverse func(walkFunc, DoNotWatchFn) error 234 // Non-recursive tree listens on Create event for every recursive 235 // watchpoint in order to automagically set a watch for every 236 // created directory. 237 switch diff := nd.Watch.dryAdd(t.rec, e|Create); { 238 case diff == none: 239 t.watchAdd(nd, c, e) 240 nd.Watch.Add(t.rec, e|omit|Create) 241 return nil 242 case diff[1] == 0: 243 // TODO(rjeczalik): cleanup this panic after implementation is stable 244 panic("eset is empty: " + nd.Name) 245 case diff[0] == 0: 246 // TODO(rjeczalik): BFS into directories and skip subtree as soon as first 247 // recursive watchpoint is encountered. 248 traverse = nd.AddDir 249 default: 250 traverse = nd.Walk 251 } 252 // TODO(rjeczalik): account every path that failed to be (re)watched 253 // and retry. 254 if err := traverse(t.recFunc(e), doNotWatch); err != nil { 255 return err 256 } 257 t.watchAdd(nd, c, e) 258 return nil 259 } 260 261 type walkWatchpointFunc func(Event, node) error 262 263 func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error { 264 type minode struct { 265 min Event 266 nd node 267 } 268 mnd := minode{nd: nd} 269 stack := []minode{mnd} 270 Traverse: 271 for n := len(stack); n != 0; n = len(stack) { 272 mnd, stack = stack[n-1], stack[:n-1] 273 // There must be no recursive watchpoints if the node has no watchpoints 274 // itself (every node in subtree rooted at recursive watchpoints must 275 // have at least nil (total) and t.rec watchpoints). 276 if len(mnd.nd.Watch) != 0 { 277 switch err := fn(mnd.min, mnd.nd); err { 278 case nil: 279 case errSkip: 280 continue Traverse 281 default: 282 return err 283 } 284 } 285 for _, nd := range mnd.nd.Child { 286 stack = append(stack, minode{mnd.nd.Watch[t.rec], nd}) 287 } 288 } 289 return nil 290 } 291 292 // Stop TODO(rjeczalik) 293 func (t *nonrecursiveTree) Stop(c chan<- EventInfo) { 294 fn := func(min Event, nd node) error { 295 // TODO(rjeczalik): aggregate watcher errors and retry; in worst case 296 // forward to the user. 297 switch diff := t.watchDelMin(min, nd, c, all); { 298 case diff == none: 299 return nil 300 case diff[1] == 0: 301 t.w.Unwatch(nd.Name) 302 default: 303 t.w.Rewatch(nd.Name, diff[0], diff[1]) 304 } 305 return nil 306 } 307 t.rw.Lock() 308 err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c 309 t.rw.Unlock() 310 dbgprintf("Stop(%p) error: %v\n", c, err) 311 } 312 313 // Close TODO(rjeczalik) 314 func (t *nonrecursiveTree) Close() error { 315 err := t.w.Close() 316 close(t.c) 317 return err 318 }