github.com/DiversionCompany/notify@v0.9.9/tree_recursive.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 // watchAdd TODO(rjeczalik) 13 func watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff { 14 diff := nd.Watch.Add(c, e) 15 if wp := nd.Child[""].Watch; len(wp) != 0 { 16 e = wp.Total() 17 diff[0] |= e 18 diff[1] |= e 19 if diff[0] == diff[1] { 20 return none 21 } 22 } 23 return diff 24 } 25 26 // watchAddInactive TODO(rjeczalik) 27 func watchAddInactive(nd node, c chan<- EventInfo, e Event) eventDiff { 28 wp := nd.Child[""].Watch 29 if wp == nil { 30 wp = make(watchpoint) 31 nd.Child[""] = node{Watch: wp} 32 } 33 diff := wp.Add(c, e) 34 e = nd.Watch.Total() 35 diff[0] |= e 36 diff[1] |= e 37 if diff[0] == diff[1] { 38 return none 39 } 40 return diff 41 } 42 43 // watchCopy TODO(rjeczalik) 44 func watchCopy(src, dst node) { 45 for c, e := range src.Watch { 46 if c == nil { 47 continue 48 } 49 watchAddInactive(dst, c, e) 50 } 51 if wpsrc := src.Child[""].Watch; len(wpsrc) != 0 { 52 wpdst := dst.Child[""].Watch 53 for c, e := range wpsrc { 54 if c == nil { 55 continue 56 } 57 wpdst.Add(c, e) 58 } 59 } 60 } 61 62 // watchDel TODO(rjeczalik) 63 func watchDel(nd node, c chan<- EventInfo, e Event) eventDiff { 64 diff := nd.Watch.Del(c, e) 65 if wp := nd.Child[""].Watch; len(wp) != 0 { 66 diffInactive := wp.Del(c, e) 67 e = wp.Total() 68 // TODO(rjeczalik): add e if e != all? 69 diff[0] |= diffInactive[0] | e 70 diff[1] |= diffInactive[1] | e 71 if diff[0] == diff[1] { 72 return none 73 } 74 } 75 return diff 76 } 77 78 // watchTotal TODO(rjeczalik) 79 func watchTotal(nd node) Event { 80 e := nd.Watch.Total() 81 if wp := nd.Child[""].Watch; len(wp) != 0 { 82 e |= wp.Total() 83 } 84 return e 85 } 86 87 // watchIsRecursive TODO(rjeczalik) 88 func watchIsRecursive(nd node) bool { 89 ok := nd.Watch.IsRecursive() 90 // TODO(rjeczalik): add a test for len(wp) != 0 change the condition. 91 if wp := nd.Child[""].Watch; len(wp) != 0 { 92 // If a watchpoint holds inactive watchpoints, it means it's a parent 93 // one, which is recursive by nature even though it may be not recursive 94 // itself. 95 ok = true 96 } 97 return ok 98 } 99 100 // recursiveTree TODO(rjeczalik) 101 type recursiveTree struct { 102 rw sync.RWMutex // protects root 103 root root 104 // TODO(rjeczalik): merge watcher + recursiveWatcher after #5 and #6 105 w interface { 106 watcher 107 recursiveWatcher 108 } 109 c chan EventInfo 110 } 111 112 // newRecursiveTree TODO(rjeczalik) 113 func newRecursiveTree(w recursiveWatcher, c chan EventInfo) *recursiveTree { 114 t := &recursiveTree{ 115 root: root{nd: newnode("")}, 116 w: struct { 117 watcher 118 recursiveWatcher 119 }{w.(watcher), w}, 120 c: c, 121 } 122 go t.dispatch() 123 return t 124 } 125 126 // dispatch TODO(rjeczalik) 127 func (t *recursiveTree) dispatch() { 128 for ei := range t.c { 129 dbgprintf("dispatching %v on %q", ei.Event(), ei.Path()) 130 t.dispatchEvent(ei) 131 } 132 } 133 134 func (t *recursiveTree) dispatchEvent(ei EventInfo) { 135 nd, ok := node{}, false 136 dir, base := split(ei.Path()) 137 fn := func(it node, isbase bool) error { 138 if isbase { 139 nd = it 140 } else { 141 it.Watch.Dispatch(ei, recursive) 142 } 143 return nil 144 } 145 t.rw.RLock() 146 defer t.rw.RUnlock() 147 // Notify recursive watchpoints found on the path. 148 if err := t.root.WalkPath(dir, fn); err != nil { 149 if !os.IsNotExist(err) { 150 dbgprint("dispatch did not reach leaf:", err) 151 } 152 return 153 } 154 // Notify parent watchpoint. 155 nd.Watch.Dispatch(ei, 0) 156 // If leaf watchpoint exists, notify it. 157 if nd, ok = nd.Child[base]; ok { 158 nd.Watch.Dispatch(ei, 0) 159 } 160 } 161 162 // Watch TODO(rjeczalik) 163 func (t *recursiveTree) Watch(path string, c chan<- EventInfo, 164 _ DoNotWatchFn, events ...Event) error { 165 if c == nil { 166 panic("notify: Watch using nil channel") 167 } 168 // Expanding with empty event set is a nop. 169 if len(events) == 0 { 170 return nil 171 } 172 path, isrec, err := cleanpath(path) 173 if err != nil { 174 return err 175 } 176 eventset := joinevents(events) 177 if isrec { 178 eventset |= recursive 179 } 180 t.rw.Lock() 181 defer t.rw.Unlock() 182 // case 1: cur is a child 183 // 184 // Look for parent watch which already covers the given path. 185 parent := node{} 186 self := false 187 err = t.root.WalkPath(path, func(nd node, isbase bool) error { 188 if watchTotal(nd) != 0 { 189 parent = nd 190 self = isbase 191 return errSkip 192 } 193 return nil 194 }) 195 cur := t.root.Add(path) // add after the walk, so it's less to traverse 196 if err == nil && parent.Watch != nil { 197 // Parent watch found. Register inactive watchpoint, so we have enough 198 // information to shrink the eventset on eventual Stop. 199 // return t.resetwatchpoint(parent, parent, c, eventset|inactive) 200 var diff eventDiff 201 if self { 202 diff = watchAdd(cur, c, eventset) 203 } else { 204 diff = watchAddInactive(parent, c, eventset) 205 } 206 switch { 207 case diff == none: 208 // the parent watchpoint already covers requested subtree with its 209 // eventset 210 case diff[0] == 0: 211 // TODO(rjeczalik): cleanup this panic after implementation is stable 212 panic("dangling watchpoint: " + parent.Name) 213 default: 214 if isrec || watchIsRecursive(parent) { 215 err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1]) 216 } else { 217 err = t.w.Rewatch(parent.Name, diff[0], diff[1]) 218 } 219 if err != nil { 220 watchDel(parent, c, diff.Event()) 221 return err 222 } 223 watchAdd(cur, c, eventset) 224 // TODO(rjeczalik): account top-most path for c 225 return nil 226 } 227 if !self { 228 watchAdd(cur, c, eventset) 229 } 230 return nil 231 } 232 // case 2: cur is new parent 233 // 234 // Look for children nodes, unwatch n-1 of them and rewatch the last one. 235 var children []node 236 fn := func(nd node) error { 237 if len(nd.Watch) == 0 { 238 return nil 239 } 240 children = append(children, nd) 241 return errSkip 242 } 243 switch must(cur.Walk(fn, nil)); len(children) { 244 case 0: 245 // no child watches, cur holds a new watch 246 case 1: 247 watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root? 248 watchCopy(children[0], cur) 249 err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]), 250 watchTotal(cur)) 251 if err != nil { 252 // Clean inactive watchpoint. The c chan did not exist before. 253 cur.Child[""] = node{} 254 delete(cur.Watch, c) 255 return err 256 } 257 return nil 258 default: 259 watchAdd(cur, c, eventset) 260 // Copy children inactive watchpoints to the new parent. 261 for _, nd := range children { 262 watchCopy(nd, cur) 263 } 264 // Watch parent subtree. 265 if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil { 266 // Clean inactive watchpoint. The c chan did not exist before. 267 cur.Child[""] = node{} 268 delete(cur.Watch, c) 269 return err 270 } 271 // Unwatch children subtrees. 272 var e error 273 for _, nd := range children { 274 if watchIsRecursive(nd) { 275 e = t.w.RecursiveUnwatch(nd.Name) 276 } else { 277 e = t.w.Unwatch(nd.Name) 278 } 279 if e != nil { 280 err = nonil(err, e) 281 // TODO(rjeczalik): child is still watched, warn all its watchpoints 282 // about possible duplicate events via Error event 283 } 284 } 285 return err 286 } 287 // case 3: cur is new, alone node 288 switch diff := watchAdd(cur, c, eventset); { 289 case diff == none: 290 // TODO(rjeczalik): cleanup this panic after implementation is stable 291 panic("watch requested but no parent watchpoint found: " + cur.Name) 292 case diff[0] == 0: 293 if isrec { 294 err = t.w.RecursiveWatch(cur.Name, diff[1]) 295 } else { 296 err = t.w.Watch(cur.Name, diff[1]) 297 } 298 if err != nil { 299 watchDel(cur, c, diff.Event()) 300 return err 301 } 302 default: 303 // TODO(rjeczalik): cleanup this panic after implementation is stable 304 panic("watch requested but no parent watchpoint found: " + cur.Name) 305 } 306 return nil 307 } 308 309 // Stop TODO(rjeczalik) 310 // 311 // TODO(rjeczalik): Split parent watchpoint - transfer watches to children 312 // if parent is no longer needed. This carries a risk that underlying 313 // watcher calls could fail - reconsider if it's worth the effort. 314 func (t *recursiveTree) Stop(c chan<- EventInfo) { 315 var err error 316 fn := func(nd node) (e error) { 317 diff := watchDel(nd, c, all) 318 switch { 319 case diff == none && watchTotal(nd) == 0: 320 // TODO(rjeczalik): There's no watchpoints deeper in the tree, 321 // probably we should remove the nodes as well. 322 return nil 323 case diff == none: 324 // Removing c from nd does not require shrinking its eventset. 325 case diff[1] == 0: 326 if watchIsRecursive(nd) { 327 e = t.w.RecursiveUnwatch(nd.Name) 328 } else { 329 e = t.w.Unwatch(nd.Name) 330 } 331 default: 332 if watchIsRecursive(nd) { 333 e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1]) 334 } else { 335 e = t.w.Rewatch(nd.Name, diff[0], diff[1]) 336 } 337 } 338 fn := func(nd node) error { 339 watchDel(nd, c, all) 340 return nil 341 } 342 err = nonil(err, e, nd.Walk(fn, nil)) 343 // TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to 344 // retry un/rewatching next time and/or let the user handle the failure 345 // vie Error event? 346 return errSkip 347 } 348 t.rw.Lock() 349 e := t.root.Walk("", fn, nil) // TODO(rjeczalik): use max root per c 350 t.rw.Unlock() 351 if e != nil { 352 err = nonil(err, e) 353 } 354 dbgprintf("Stop(%p) error: %v\n", c, err) 355 } 356 357 // Close TODO(rjeczalik) 358 func (t *recursiveTree) Close() error { 359 err := t.w.Close() 360 close(t.c) 361 return err 362 }