github.com/dgallion1/notify@v0.9.3-0.20201128171805-931189d936e0/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, 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 }