gfsnotify.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  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. "github.com/gogf/gf/container/gset"
  11. "github.com/gogf/gf/errors/gcode"
  12. "github.com/gogf/gf/errors/gerror"
  13. "github.com/gogf/gf/internal/intlog"
  14. "sync"
  15. "time"
  16. "github.com/fsnotify/fsnotify"
  17. "github.com/gogf/gf/container/glist"
  18. "github.com/gogf/gf/container/gmap"
  19. "github.com/gogf/gf/container/gqueue"
  20. "github.com/gogf/gf/container/gtype"
  21. "github.com/gogf/gf/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. const (
  51. CREATE Op = 1 << iota
  52. WRITE
  53. REMOVE
  54. RENAME
  55. CHMOD
  56. )
  57. const (
  58. repeatEventFilterDuration = time.Millisecond // Duration for repeated event filter.
  59. callbackExitEventPanicStr = "exit" // Custom exit event for internal usage.
  60. )
  61. var (
  62. mu sync.Mutex // Mutex for concurrent safety of defaultWatcher.
  63. defaultWatcher *Watcher // Default watcher.
  64. callbackIdMap = gmap.NewIntAnyMap(true) // Id to callback mapping.
  65. callbackIdGenerator = gtype.NewInt() // Atomic id generator for callback.
  66. )
  67. // New creates and returns a new watcher.
  68. // Note that the watcher number is limited by the file handle setting of the system.
  69. // Eg: fs.inotify.max_user_instances system variable in linux systems.
  70. func New() (*Watcher, error) {
  71. w := &Watcher{
  72. cache: gcache.New(),
  73. events: gqueue.New(),
  74. nameSet: gset.NewStrSet(true),
  75. closeChan: make(chan struct{}),
  76. callbacks: gmap.NewStrAnyMap(true),
  77. }
  78. if watcher, err := fsnotify.NewWatcher(); err == nil {
  79. w.watcher = watcher
  80. } else {
  81. intlog.Printf(context.TODO(), "New watcher failed: %v", err)
  82. return nil, err
  83. }
  84. w.watchLoop()
  85. w.eventLoop()
  86. return w, nil
  87. }
  88. // Add monitors `path` using default watcher with callback function `callbackFunc`.
  89. // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
  90. func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
  91. w, err := getDefaultWatcher()
  92. if err != nil {
  93. return nil, err
  94. }
  95. return w.Add(path, callbackFunc, recursive...)
  96. }
  97. // AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`.
  98. // If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error
  99. // if it's called twice with the same `name`.
  100. //
  101. // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
  102. func AddOnce(name, 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.AddOnce(name, path, callbackFunc, recursive...)
  108. }
  109. // Remove removes all monitoring callbacks of given `path` from watcher recursively.
  110. func Remove(path string) error {
  111. w, err := getDefaultWatcher()
  112. if err != nil {
  113. return err
  114. }
  115. return w.Remove(path)
  116. }
  117. // RemoveCallback removes specified callback with given id from watcher.
  118. func RemoveCallback(callbackId int) error {
  119. w, err := getDefaultWatcher()
  120. if err != nil {
  121. return err
  122. }
  123. callback := (*Callback)(nil)
  124. if r := callbackIdMap.Get(callbackId); r != nil {
  125. callback = r.(*Callback)
  126. }
  127. if callback == nil {
  128. return gerror.NewCodef(gcode.CodeInvalidParameter, `callback for id %d not found`, callbackId)
  129. }
  130. w.RemoveCallback(callbackId)
  131. return nil
  132. }
  133. // Exit is only used in the callback function, which can be used to remove current callback
  134. // of itself from the watcher.
  135. func Exit() {
  136. panic(callbackExitEventPanicStr)
  137. }
  138. // getDefaultWatcher creates and returns the default watcher.
  139. // This is used for lazy initialization purpose.
  140. func getDefaultWatcher() (*Watcher, error) {
  141. mu.Lock()
  142. defer mu.Unlock()
  143. if defaultWatcher != nil {
  144. return defaultWatcher, nil
  145. }
  146. var err error
  147. defaultWatcher, err = New()
  148. return defaultWatcher, err
  149. }