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