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