github.com/Hellyna/notify@v0.0.0-20210101060149-8ebdd4ef22cf/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 { 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 var nd node 83 var eset = internal 84 t.rw.Lock() 85 t.root.WalkPath(ei.Path(), func(it node, _ bool) error { 86 if e := it.Watch[t.rec]; e != 0 && e > eset { 87 eset = e 88 } 89 nd = it 90 return nil 91 }) 92 if eset == internal { 93 t.rw.Unlock() 94 continue 95 } 96 err := nd.Add(ei.Path()).AddDir(t.recFunc(eset), nil) 97 t.rw.Unlock() 98 if err != nil { 99 dbgprintf("internal(%p) error: %v", rec, err) 100 } 101 } 102 } 103 104 // watchAdd TODO(rjeczalik) 105 func (t *nonrecursiveTree) watchAdd(nd node, c chan<- EventInfo, e Event) eventDiff { 106 if e&recursive != 0 { 107 diff := nd.Watch.Add(t.rec, e|Create|omit) 108 nd.Watch.Add(c, e) 109 return diff 110 } 111 return nd.Watch.Add(c, e) 112 } 113 114 // watchDelMin TODO(rjeczalik) 115 func (t *nonrecursiveTree) watchDelMin(min Event, nd node, c chan<- EventInfo, e Event) eventDiff { 116 old, ok := nd.Watch[t.rec] 117 if ok { 118 nd.Watch[t.rec] = min 119 } 120 diff := nd.Watch.Del(c, e) 121 if ok { 122 switch old &^= diff[0] &^ diff[1]; { 123 case old|internal == internal: 124 delete(nd.Watch, t.rec) 125 if set, ok := nd.Watch[nil]; ok && len(nd.Watch) == 1 && set == 0 { 126 delete(nd.Watch, nil) 127 } 128 default: 129 nd.Watch.Add(t.rec, old|Create) 130 switch { 131 case diff == none: 132 case diff[1]|Create == diff[0]: 133 diff = none 134 default: 135 diff[1] |= Create 136 } 137 } 138 } 139 return diff 140 } 141 142 // watchDel TODO(rjeczalik) 143 func (t *nonrecursiveTree) watchDel(nd node, c chan<- EventInfo, e Event) eventDiff { 144 return t.watchDelMin(0, nd, c, e) 145 } 146 147 // Watch TODO(rjeczalik) 148 func (t *nonrecursiveTree) Watch(path string, c chan<- EventInfo, 149 doNotWatch DoNotWatchFn, events ...Event) error { 150 if c == nil { 151 panic("notify: Watch using nil channel") 152 } 153 // Expanding with empty event set is a nop. 154 if len(events) == 0 { 155 return nil 156 } 157 path, isrec, err := cleanpath(path) 158 if err != nil { 159 return err 160 } 161 eset := joinevents(events) 162 t.rw.Lock() 163 defer t.rw.Unlock() 164 nd := t.root.Add(path) 165 if isrec { 166 return t.watchrec(nd, c, eset|recursive, doNotWatch) 167 } 168 return t.watch(nd, c, eset) 169 } 170 171 func (t *nonrecursiveTree) watch(nd node, c chan<- EventInfo, e Event) (err error) { 172 diff := nd.Watch.Add(c, e) 173 switch { 174 case diff == none: 175 return nil 176 case diff[1] == 0: 177 // TODO(rjeczalik): cleanup this panic after implementation is stable 178 panic("eset is empty: " + nd.Name) 179 case diff[0] == 0: 180 err = t.w.Watch(nd.Name, diff[1]) 181 default: 182 err = t.w.Rewatch(nd.Name, diff[0], diff[1]) 183 } 184 if err != nil { 185 nd.Watch.Del(c, diff.Event()) 186 return err 187 } 188 return nil 189 } 190 191 func (t *nonrecursiveTree) recFunc(e Event) walkFunc { 192 return func(nd node) (err error) { 193 switch diff := nd.Watch.Add(t.rec, e|omit|Create); { 194 case diff == none: 195 case diff[1] == 0: 196 // TODO(rjeczalik): cleanup this panic after implementation is stable 197 panic("eset is empty: " + nd.Name) 198 case diff[0] == 0: 199 err = t.w.Watch(nd.Name, diff[1]) 200 default: 201 err = t.w.Rewatch(nd.Name, diff[0], diff[1]) 202 } 203 return 204 } 205 } 206 207 func (t *nonrecursiveTree) watchrec(nd node, c chan<- EventInfo, e Event, 208 doNotWatch DoNotWatchFn) error { 209 var traverse func(walkFunc, DoNotWatchFn) error 210 // Non-recursive tree listens on Create event for every recursive 211 // watchpoint in order to automagically set a watch for every 212 // created directory. 213 switch diff := nd.Watch.dryAdd(t.rec, e|Create); { 214 case diff == none: 215 t.watchAdd(nd, c, e) 216 nd.Watch.Add(t.rec, e|omit|Create) 217 return nil 218 case diff[1] == 0: 219 // TODO(rjeczalik): cleanup this panic after implementation is stable 220 panic("eset is empty: " + nd.Name) 221 case diff[0] == 0: 222 // TODO(rjeczalik): BFS into directories and skip subtree as soon as first 223 // recursive watchpoint is encountered. 224 traverse = nd.AddDir 225 default: 226 traverse = nd.Walk 227 } 228 // TODO(rjeczalik): account every path that failed to be (re)watched 229 // and retry. 230 if err := traverse(t.recFunc(e), doNotWatch); err != nil { 231 return err 232 } 233 t.watchAdd(nd, c, e) 234 return nil 235 } 236 237 type walkWatchpointFunc func(Event, node) error 238 239 func (t *nonrecursiveTree) walkWatchpoint(nd node, fn walkWatchpointFunc) error { 240 type minode struct { 241 min Event 242 nd node 243 } 244 mnd := minode{nd: nd} 245 stack := []minode{mnd} 246 Traverse: 247 for n := len(stack); n != 0; n = len(stack) { 248 mnd, stack = stack[n-1], stack[:n-1] 249 // There must be no recursive watchpoints if the node has no watchpoints 250 // itself (every node in subtree rooted at recursive watchpoints must 251 // have at least nil (total) and t.rec watchpoints). 252 if len(mnd.nd.Watch) != 0 { 253 switch err := fn(mnd.min, mnd.nd); err { 254 case nil: 255 case errSkip: 256 continue Traverse 257 default: 258 return err 259 } 260 } 261 for _, nd := range mnd.nd.Child { 262 stack = append(stack, minode{mnd.nd.Watch[t.rec], nd}) 263 } 264 } 265 return nil 266 } 267 268 // Stop TODO(rjeczalik) 269 func (t *nonrecursiveTree) Stop(c chan<- EventInfo) { 270 fn := func(min Event, nd node) error { 271 // TODO(rjeczalik): aggregate watcher errors and retry; in worst case 272 // forward to the user. 273 switch diff := t.watchDelMin(min, nd, c, all); { 274 case diff == none: 275 return nil 276 case diff[1] == 0: 277 t.w.Unwatch(nd.Name) 278 default: 279 t.w.Rewatch(nd.Name, diff[0], diff[1]) 280 } 281 return nil 282 } 283 t.rw.Lock() 284 err := t.walkWatchpoint(t.root.nd, fn) // TODO(rjeczalik): store max root per c 285 t.rw.Unlock() 286 dbgprintf("Stop(%p) error: %v\n", c, err) 287 } 288 289 // Close TODO(rjeczalik) 290 func (t *nonrecursiveTree) Close() error { 291 err := t.w.Close() 292 close(t.c) 293 return err 294 }