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