github.com/checksum/notify@v0.0.0-20190119234841-59aa2d88664f/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 "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, 157 doNotWatch func(string) bool, events ...Event) error { 158 if c == nil { 159 panic("notify: Watch using nil channel") 160 } 161 // Expanding with empty event set is a nop. 162 if len(events) == 0 { 163 return nil 164 } 165 path, isrec, err := cleanpath(path) 166 if err != nil { 167 return err 168 } 169 eventset := joinevents(events) 170 if isrec { 171 eventset |= recursive 172 } 173 t.rw.Lock() 174 defer t.rw.Unlock() 175 // case 1: cur is a child 176 // 177 // Look for parent watch which already covers the given path. 178 parent := node{} 179 self := false 180 err = t.root.WalkPath(path, func(nd node, isbase bool) error { 181 if watchTotal(nd) != 0 { 182 parent = nd 183 self = isbase 184 return errSkip 185 } 186 return nil 187 }) 188 cur := t.root.Add(path) // add after the walk, so it's less to traverse 189 if err == nil && parent.Watch != nil { 190 // Parent watch found. Register inactive watchpoint, so we have enough 191 // information to shrink the eventset on eventual Stop. 192 // return t.resetwatchpoint(parent, parent, c, eventset|inactive) 193 var diff eventDiff 194 if self { 195 diff = watchAdd(cur, c, eventset) 196 } else { 197 diff = watchAddInactive(parent, c, eventset) 198 } 199 switch { 200 case diff == none: 201 // the parent watchpoint already covers requested subtree with its 202 // eventset 203 case diff[0] == 0: 204 // TODO(rjeczalik): cleanup this panic after implementation is stable 205 panic("dangling watchpoint: " + parent.Name) 206 default: 207 if isrec || watchIsRecursive(parent) { 208 err = t.w.RecursiveRewatch(parent.Name, parent.Name, diff[0], diff[1]) 209 } else { 210 err = t.w.Rewatch(parent.Name, diff[0], diff[1]) 211 } 212 if err != nil { 213 watchDel(parent, c, diff.Event()) 214 return err 215 } 216 watchAdd(cur, c, eventset) 217 // TODO(rjeczalik): account top-most path for c 218 return nil 219 } 220 if !self { 221 watchAdd(cur, c, eventset) 222 } 223 return nil 224 } 225 // case 2: cur is new parent 226 // 227 // Look for children nodes, unwatch n-1 of them and rewatch the last one. 228 var children []node 229 fn := func(nd node) error { 230 if len(nd.Watch) == 0 { 231 return nil 232 } 233 children = append(children, nd) 234 return errSkip 235 } 236 switch must(cur.Walk(fn)); len(children) { 237 case 0: 238 // no child watches, cur holds a new watch 239 case 1: 240 watchAdd(cur, c, eventset) // TODO(rjeczalik): update cache c subtree root? 241 watchCopy(children[0], cur) 242 err = t.w.RecursiveRewatch(children[0].Name, cur.Name, watchTotal(children[0]), 243 watchTotal(cur)) 244 if err != nil { 245 // Clean inactive watchpoint. The c chan did not exist before. 246 cur.Child[""] = node{} 247 delete(cur.Watch, c) 248 return err 249 } 250 return nil 251 default: 252 watchAdd(cur, c, eventset) 253 // Copy children inactive watchpoints to the new parent. 254 for _, nd := range children { 255 watchCopy(nd, cur) 256 } 257 // Watch parent subtree. 258 if err = t.w.RecursiveWatch(cur.Name, watchTotal(cur)); err != nil { 259 // Clean inactive watchpoint. The c chan did not exist before. 260 cur.Child[""] = node{} 261 delete(cur.Watch, c) 262 return err 263 } 264 // Unwatch children subtrees. 265 var e error 266 for _, nd := range children { 267 if watchIsRecursive(nd) { 268 e = t.w.RecursiveUnwatch(nd.Name) 269 } else { 270 e = t.w.Unwatch(nd.Name) 271 } 272 if e != nil { 273 err = nonil(err, e) 274 // TODO(rjeczalik): child is still watched, warn all its watchpoints 275 // about possible duplicate events via Error event 276 } 277 } 278 return err 279 } 280 // case 3: cur is new, alone node 281 switch diff := watchAdd(cur, c, eventset); { 282 case diff == none: 283 // TODO(rjeczalik): cleanup this panic after implementation is stable 284 panic("watch requested but no parent watchpoint found: " + cur.Name) 285 case diff[0] == 0: 286 if isrec { 287 err = t.w.RecursiveWatch(cur.Name, diff[1]) 288 } else { 289 err = t.w.Watch(cur.Name, diff[1]) 290 } 291 if err != nil { 292 watchDel(cur, c, diff.Event()) 293 return err 294 } 295 default: 296 // TODO(rjeczalik): cleanup this panic after implementation is stable 297 panic("watch requested but no parent watchpoint found: " + cur.Name) 298 } 299 return nil 300 } 301 302 // Stop TODO(rjeczalik) 303 // 304 // TODO(rjeczalik): Split parent watchpoint - transfer watches to children 305 // if parent is no longer needed. This carries a risk that underlying 306 // watcher calls could fail - reconsider if it's worth the effort. 307 func (t *recursiveTree) Stop(c chan<- EventInfo) { 308 var err error 309 fn := func(nd node) (e error) { 310 diff := watchDel(nd, c, all) 311 switch { 312 case diff == none && watchTotal(nd) == 0: 313 // TODO(rjeczalik): There's no watchpoints deeper in the tree, 314 // probably we should remove the nodes as well. 315 return nil 316 case diff == none: 317 // Removing c from nd does not require shrinking its eventset. 318 case diff[1] == 0: 319 if watchIsRecursive(nd) { 320 e = t.w.RecursiveUnwatch(nd.Name) 321 } else { 322 e = t.w.Unwatch(nd.Name) 323 } 324 default: 325 if watchIsRecursive(nd) { 326 e = t.w.RecursiveRewatch(nd.Name, nd.Name, diff[0], diff[1]) 327 } else { 328 e = t.w.Rewatch(nd.Name, diff[0], diff[1]) 329 } 330 } 331 fn := func(nd node) error { 332 watchDel(nd, c, all) 333 return nil 334 } 335 err = nonil(err, e, nd.Walk(fn)) 336 // TODO(rjeczalik): if e != nil store dummy chan in nd.Watch just to 337 // retry un/rewatching next time and/or let the user handle the failure 338 // vie Error event? 339 return errSkip 340 } 341 t.rw.Lock() 342 e := t.root.Walk("", fn) // TODO(rjeczalik): use max root per c 343 t.rw.Unlock() 344 if e != nil { 345 err = nonil(err, e) 346 } 347 dbgprintf("Stop(%p) error: %v\n", c, err) 348 } 349 350 // Close TODO(rjeczalik) 351 func (t *recursiveTree) Close() error { 352 err := t.w.Close() 353 close(t.c) 354 return err 355 }