github.com/wangyougui/gf/v2@v2.6.5/os/gfsnotify/gfsnotify.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 provides a platform-independent interface for file system notifications. 8 package gfsnotify 9 10 import ( 11 "context" 12 "sync" 13 "time" 14 15 "github.com/fsnotify/fsnotify" 16 17 "github.com/wangyougui/gf/v2/container/glist" 18 "github.com/wangyougui/gf/v2/container/gmap" 19 "github.com/wangyougui/gf/v2/container/gqueue" 20 "github.com/wangyougui/gf/v2/container/gset" 21 "github.com/wangyougui/gf/v2/container/gtype" 22 "github.com/wangyougui/gf/v2/errors/gcode" 23 "github.com/wangyougui/gf/v2/errors/gerror" 24 "github.com/wangyougui/gf/v2/internal/intlog" 25 "github.com/wangyougui/gf/v2/os/gcache" 26 ) 27 28 // Watcher is the monitor for file changes. 29 type Watcher struct { 30 watcher *fsnotify.Watcher // Underlying fsnotify object. 31 events *gqueue.Queue // Used for internal event management. 32 cache *gcache.Cache // Used for repeated event filter. 33 nameSet *gset.StrSet // Used for AddOnce feature. 34 callbacks *gmap.StrAnyMap // Path(file/folder) to callbacks mapping. 35 closeChan chan struct{} // Used for watcher closing notification. 36 } 37 38 // Callback is the callback function for Watcher. 39 type Callback struct { 40 Id int // Unique id for callback object. 41 Func func(event *Event) // Callback function. 42 Path string // Bound file path (absolute). 43 name string // Registered name for AddOnce. 44 elem *glist.Element // Element in the callbacks of watcher. 45 recursive bool // Is bound to path recursively or not. 46 } 47 48 // Event is the event produced by underlying fsnotify. 49 type Event struct { 50 event fsnotify.Event // Underlying event. 51 Path string // Absolute file path. 52 Op Op // File operation. 53 Watcher *Watcher // Parent watcher. 54 } 55 56 // Op is the bits union for file operations. 57 type Op uint32 58 59 // internalPanic is the custom panic for internal usage. 60 type internalPanic string 61 62 const ( 63 CREATE Op = 1 << iota 64 WRITE 65 REMOVE 66 RENAME 67 CHMOD 68 ) 69 70 const ( 71 repeatEventFilterDuration = time.Millisecond // Duration for repeated event filter. 72 callbackExitEventPanicStr internalPanic = "exit" // Custom exit event for internal usage. 73 ) 74 75 var ( 76 mu sync.Mutex // Mutex for concurrent safety of defaultWatcher. 77 defaultWatcher *Watcher // Default watcher. 78 callbackIdMap = gmap.NewIntAnyMap(true) // Id to callback mapping. 79 callbackIdGenerator = gtype.NewInt() // Atomic id generator for callback. 80 ) 81 82 // New creates and returns a new watcher. 83 // Note that the watcher number is limited by the file handle setting of the system. 84 // Eg: fs.inotify.max_user_instances system variable in linux systems. 85 func New() (*Watcher, error) { 86 w := &Watcher{ 87 cache: gcache.New(), 88 events: gqueue.New(), 89 nameSet: gset.NewStrSet(true), 90 closeChan: make(chan struct{}), 91 callbacks: gmap.NewStrAnyMap(true), 92 } 93 if watcher, err := fsnotify.NewWatcher(); err == nil { 94 w.watcher = watcher 95 } else { 96 intlog.Printf(context.TODO(), "New watcher failed: %v", err) 97 return nil, err 98 } 99 w.watchLoop() 100 w.eventLoop() 101 return w, nil 102 } 103 104 // Add monitors `path` using default watcher with callback function `callbackFunc`. 105 // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default. 106 func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { 107 w, err := getDefaultWatcher() 108 if err != nil { 109 return nil, err 110 } 111 return w.Add(path, callbackFunc, recursive...) 112 } 113 114 // AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`. 115 // If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error 116 // if it's called twice with the same `name`. 117 // 118 // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default. 119 func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { 120 w, err := getDefaultWatcher() 121 if err != nil { 122 return nil, err 123 } 124 return w.AddOnce(name, path, callbackFunc, recursive...) 125 } 126 127 // Remove removes all monitoring callbacks of given `path` from watcher recursively. 128 func Remove(path string) error { 129 w, err := getDefaultWatcher() 130 if err != nil { 131 return err 132 } 133 return w.Remove(path) 134 } 135 136 // RemoveCallback removes specified callback with given id from watcher. 137 func RemoveCallback(callbackId int) error { 138 w, err := getDefaultWatcher() 139 if err != nil { 140 return err 141 } 142 callback := (*Callback)(nil) 143 if r := callbackIdMap.Get(callbackId); r != nil { 144 callback = r.(*Callback) 145 } 146 if callback == nil { 147 return gerror.NewCodef(gcode.CodeInvalidParameter, `callback for id %d not found`, callbackId) 148 } 149 w.RemoveCallback(callbackId) 150 return nil 151 } 152 153 // Exit is only used in the callback function, which can be used to remove current callback 154 // of itself from the watcher. 155 func Exit() { 156 panic(callbackExitEventPanicStr) 157 } 158 159 // getDefaultWatcher creates and returns the default watcher. 160 // This is used for lazy initialization purpose. 161 func getDefaultWatcher() (*Watcher, error) { 162 mu.Lock() 163 defer mu.Unlock() 164 if defaultWatcher != nil { 165 return defaultWatcher, nil 166 } 167 var err error 168 defaultWatcher, err = New() 169 return defaultWatcher, err 170 }