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