github.com/wangyougui/gf/v2@v2.6.5/os/gfsnotify/gfsnotify_watcher_loop.go (about) 1 // Copyright GoFrame Author(https://goframe.org). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/wangyougui/gf. 6 7 package gfsnotify 8 9 import ( 10 "context" 11 "github.com/wangyougui/gf/v2/errors/gcode" 12 "github.com/wangyougui/gf/v2/errors/gerror" 13 14 "github.com/wangyougui/gf/v2/container/glist" 15 "github.com/wangyougui/gf/v2/internal/intlog" 16 ) 17 18 // watchLoop starts the loop for event listening from underlying inotify monitor. 19 func (w *Watcher) watchLoop() { 20 go func() { 21 for { 22 select { 23 // Close event. 24 case <-w.closeChan: 25 return 26 27 // Event listening. 28 case ev := <-w.watcher.Events: 29 // Filter the repeated event in custom duration. 30 _, err := w.cache.SetIfNotExist( 31 context.Background(), 32 ev.String(), 33 func(ctx context.Context) (value interface{}, err error) { 34 w.events.Push(&Event{ 35 event: ev, 36 Path: ev.Name, 37 Op: Op(ev.Op), 38 Watcher: w, 39 }) 40 return struct{}{}, nil 41 }, repeatEventFilterDuration, 42 ) 43 if err != nil { 44 intlog.Errorf(context.TODO(), `%+v`, err) 45 } 46 47 case err := <-w.watcher.Errors: 48 intlog.Errorf(context.TODO(), `%+v`, err) 49 } 50 } 51 }() 52 } 53 54 // eventLoop is the core event handler. 55 func (w *Watcher) eventLoop() { 56 go func() { 57 for { 58 if v := w.events.Pop(); v != nil { 59 event := v.(*Event) 60 // If there's no any callback of this path, it removes it from monitor. 61 callbacks := w.getCallbacks(event.Path) 62 if len(callbacks) == 0 { 63 _ = w.watcher.Remove(event.Path) 64 continue 65 } 66 switch { 67 case event.IsRemove(): 68 // It should check again the existence of the path. 69 // It adds it back to the monitor if it still exists. 70 if fileExists(event.Path) { 71 // It adds the path back to monitor. 72 // We need no worry about the repeat adding. 73 if err := w.watcher.Add(event.Path); err != nil { 74 intlog.Errorf(context.TODO(), `%+v`, err) 75 } else { 76 intlog.Printf(context.TODO(), "fake remove event, watcher re-adds monitor for: %s", event.Path) 77 } 78 // Change the event to RENAME, which means it renames itself to its origin name. 79 event.Op = RENAME 80 } 81 82 case event.IsRename(): 83 // It should check again the existence of the path. 84 // It adds it back to the monitor if it still exists. 85 // Especially Some editors might do RENAME and then CHMOD when it's editing file. 86 if fileExists(event.Path) { 87 // It might lost the monitoring for the path, so we add the path back to monitor. 88 // We need no worry about the repeat adding. 89 if err := w.watcher.Add(event.Path); err != nil { 90 intlog.Errorf(context.TODO(), `%+v`, err) 91 } else { 92 intlog.Printf(context.TODO(), "fake rename event, watcher re-adds monitor for: %s", event.Path) 93 } 94 // Change the event to CHMOD. 95 event.Op = CHMOD 96 } 97 98 case event.IsCreate(): 99 // ========================================= 100 // Note that it here just adds the path to monitor without any callback registering, 101 // because its parent already has the callbacks. 102 // ========================================= 103 if fileIsDir(event.Path) { 104 // If it's a folder, it then does adding recursively to monitor. 105 for _, subPath := range fileAllDirs(event.Path) { 106 if fileIsDir(subPath) { 107 if err := w.watcher.Add(subPath); err != nil { 108 intlog.Errorf(context.TODO(), `%+v`, err) 109 } else { 110 intlog.Printf(context.TODO(), "folder creation event, watcher adds monitor for: %s", subPath) 111 } 112 } 113 } 114 } else { 115 // If it's a file, it directly adds it to monitor. 116 if err := w.watcher.Add(event.Path); err != nil { 117 intlog.Errorf(context.TODO(), `%+v`, err) 118 } else { 119 intlog.Printf(context.TODO(), "file creation event, watcher adds monitor for: %s", event.Path) 120 } 121 } 122 } 123 // Calling the callbacks in order. 124 for _, callback := range callbacks { 125 go func(callback *Callback) { 126 defer func() { 127 if err := recover(); err != nil { 128 switch err { 129 case callbackExitEventPanicStr: 130 w.RemoveCallback(callback.Id) 131 default: 132 if e, ok := err.(error); ok { 133 panic(gerror.WrapCode(gcode.CodeInternalPanic, e)) 134 } 135 panic(err) 136 } 137 } 138 }() 139 callback.Func(event) 140 }(callback) 141 } 142 } else { 143 break 144 } 145 } 146 }() 147 } 148 149 // getCallbacks searches and returns all callbacks with given `path`. 150 // It also searches its parents for callbacks if they're recursive. 151 func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) { 152 // Firstly add the callbacks of itself. 153 if v := w.callbacks.Get(path); v != nil { 154 for _, v := range v.(*glist.List).FrontAll() { 155 callback := v.(*Callback) 156 callbacks = append(callbacks, callback) 157 } 158 } 159 // Secondly searches its direct parent for callbacks. 160 // It is special handling here, which is the different between `recursive` and `not recursive` logic 161 // for direct parent folder of `path` that events are from. 162 dirPath := fileDir(path) 163 if v := w.callbacks.Get(dirPath); v != nil { 164 for _, v := range v.(*glist.List).FrontAll() { 165 callback := v.(*Callback) 166 callbacks = append(callbacks, callback) 167 } 168 } 169 // Lastly searches all the parents of directory of `path` recursively for callbacks. 170 for { 171 parentDirPath := fileDir(dirPath) 172 if parentDirPath == dirPath { 173 break 174 } 175 if v := w.callbacks.Get(parentDirPath); v != nil { 176 for _, v := range v.(*glist.List).FrontAll() { 177 callback := v.(*Callback) 178 if callback.recursive { 179 callbacks = append(callbacks, callback) 180 } 181 } 182 } 183 dirPath = parentDirPath 184 } 185 return 186 }