github.com/gogf/gf/v2@v2.7.4/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/gogf/gf. 6 7 package gfsnotify 8 9 import ( 10 "context" 11 12 "github.com/gogf/gf/v2/errors/gcode" 13 "github.com/gogf/gf/v2/errors/gerror" 14 15 "github.com/gogf/gf/v2/container/glist" 16 "github.com/gogf/gf/v2/internal/intlog" 17 ) 18 19 // watchLoop starts the loop for event listening from underlying inotify monitor. 20 func (w *Watcher) watchLoop() { 21 for { 22 select { 23 // Close event. 24 case <-w.closeChan: 25 return 26 27 // Event listening. 28 case ev, ok := <-w.watcher.Events: 29 if !ok { 30 return 31 } 32 // Filter the repeated event in custom duration. 33 _, err := w.cache.SetIfNotExist( 34 context.Background(), 35 ev.String(), 36 func(ctx context.Context) (value interface{}, err error) { 37 w.events.Push(&Event{ 38 event: ev, 39 Path: ev.Name, 40 Op: Op(ev.Op), 41 Watcher: w, 42 }) 43 return struct{}{}, nil 44 }, repeatEventFilterDuration, 45 ) 46 if err != nil { 47 intlog.Errorf(context.TODO(), `%+v`, err) 48 } 49 50 case err := <-w.watcher.Errors: 51 intlog.Errorf(context.TODO(), `%+v`, err) 52 } 53 } 54 } 55 56 // eventLoop is the core event handler. 57 func (w *Watcher) eventLoop() { 58 for { 59 if v := w.events.Pop(); v != nil { 60 event := v.(*Event) 61 // If there's no any callback of this path, it removes it from monitor. 62 callbacks := w.getCallbacks(event.Path) 63 if len(callbacks) == 0 { 64 _ = w.watcher.Remove(event.Path) 65 continue 66 } 67 switch { 68 case event.IsRemove(): 69 // It should check again the existence of the path. 70 // It adds it back to the monitor if it still exists. 71 if fileExists(event.Path) { 72 // It adds the path back to monitor. 73 // We need no worry about the repeat adding. 74 if err := w.watcher.Add(event.Path); err != nil { 75 intlog.Errorf(context.TODO(), `%+v`, err) 76 } else { 77 intlog.Printf(context.TODO(), "fake remove event, watcher re-adds monitor for: %s", event.Path) 78 } 79 // Change the event to RENAME, which means it renames itself to its origin name. 80 event.Op = RENAME 81 } 82 83 case event.IsRename(): 84 // It should check again the existence of the path. 85 // It adds it back to the monitor if it still exists. 86 // Especially Some editors might do RENAME and then CHMOD when it's editing file. 87 if fileExists(event.Path) { 88 // It might lost the monitoring for the path, so we add the path back to monitor. 89 // We need no worry about the repeat adding. 90 if err := w.watcher.Add(event.Path); err != nil { 91 intlog.Errorf(context.TODO(), `%+v`, err) 92 } else { 93 intlog.Printf(context.TODO(), "fake rename event, watcher re-adds monitor for: %s", event.Path) 94 } 95 // Change the event to CHMOD. 96 event.Op = CHMOD 97 } 98 99 case event.IsCreate(): 100 // ========================================= 101 // Note that it here just adds the path to monitor without any callback registering, 102 // because its parent already has the callbacks. 103 // ========================================= 104 if fileIsDir(event.Path) { 105 // If it's a folder, it then does adding recursively to monitor. 106 for _, subPath := range fileAllDirs(event.Path) { 107 if fileIsDir(subPath) { 108 if err := w.watcher.Add(subPath); err != nil { 109 intlog.Errorf(context.TODO(), `%+v`, err) 110 } else { 111 intlog.Printf(context.TODO(), "folder creation event, watcher adds monitor for: %s", subPath) 112 } 113 } 114 } 115 } else { 116 // If it's a file, it directly adds it to monitor. 117 if err := w.watcher.Add(event.Path); err != nil { 118 intlog.Errorf(context.TODO(), `%+v`, err) 119 } else { 120 intlog.Printf(context.TODO(), "file creation event, watcher adds monitor for: %s", event.Path) 121 } 122 } 123 } 124 // Calling the callbacks in order. 125 for _, callback := range callbacks { 126 go func(callback *Callback) { 127 defer func() { 128 if err := recover(); err != nil { 129 switch err { 130 case callbackExitEventPanicStr: 131 w.RemoveCallback(callback.Id) 132 default: 133 if e, ok := err.(error); ok { 134 panic(gerror.WrapCode(gcode.CodeInternalPanic, e)) 135 } 136 panic(err) 137 } 138 } 139 }() 140 callback.Func(event) 141 }(callback) 142 } 143 } else { 144 break 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 }