gfsnotify.go 5.5 KB

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