gfsnotify_watcher.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. // Copyright 2018 gf Author(https://github.com/gogf/gf). 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
  7. import (
  8. "errors"
  9. "fmt"
  10. "github.com/gogf/gf/internal/intlog"
  11. "github.com/gogf/gf/container/glist"
  12. )
  13. // Add monitors <path> with callback function <callbackFunc> to the watcher.
  14. // The optional parameter <recursive> specifies whether monitoring the <path> recursively,
  15. // which is true in default.
  16. func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
  17. return w.AddOnce("", path, callbackFunc, recursive...)
  18. }
  19. // AddOnce monitors <path> with callback function <callbackFunc> only once using unique name
  20. // <name> to the watcher. If AddOnce is called multiple times with the same <name> parameter,
  21. // <path> is only added to monitor once.
  22. // It returns error if it's called twice with the same <name>.
  23. //
  24. // The optional parameter <recursive> specifies whether monitoring the <path> recursively,
  25. // which is true in default.
  26. func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
  27. w.nameSet.AddIfNotExistFuncLock(name, func() bool {
  28. // Firstly add the path to watcher.
  29. callback, err = w.addWithCallbackFunc(name, path, callbackFunc, recursive...)
  30. if err != nil {
  31. return false
  32. }
  33. // If it's recursive adding, it then adds all sub-folders to the monitor.
  34. // NOTE:
  35. // 1. It only recursively adds **folders** to the monitor, NOT files,
  36. // because if the folders are monitored and their sub-files are also monitored.
  37. // 2. It bounds no callbacks to the folders, because it will search the callbacks
  38. // from its parent recursively if any event produced.
  39. if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) {
  40. for _, subPath := range fileAllDirs(path) {
  41. if fileIsDir(subPath) {
  42. if err := w.watcher.Add(subPath); err != nil {
  43. intlog.Error(err)
  44. } else {
  45. intlog.Printf("watcher adds monitor for: %s", subPath)
  46. }
  47. }
  48. }
  49. }
  50. if name == "" {
  51. return false
  52. }
  53. return true
  54. })
  55. return
  56. }
  57. // addWithCallbackFunc adds the path to underlying monitor, creates and returns a callback object.
  58. func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
  59. // Check and convert the given path to absolute path.
  60. if t := fileRealPath(path); t == "" {
  61. return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
  62. } else {
  63. path = t
  64. }
  65. // Create callback object.
  66. callback = &Callback{
  67. Id: callbackIdGenerator.Add(1),
  68. Func: callbackFunc,
  69. Path: path,
  70. name: name,
  71. recursive: true,
  72. }
  73. if len(recursive) > 0 {
  74. callback.recursive = recursive[0]
  75. }
  76. // Register the callback to watcher.
  77. w.callbacks.LockFunc(func(m map[string]interface{}) {
  78. list := (*glist.List)(nil)
  79. if v, ok := m[path]; !ok {
  80. list = glist.New(true)
  81. m[path] = list
  82. } else {
  83. list = v.(*glist.List)
  84. }
  85. callback.elem = list.PushBack(callback)
  86. })
  87. // Add the path to underlying monitor.
  88. if err := w.watcher.Add(path); err != nil {
  89. intlog.Error(err)
  90. } else {
  91. intlog.Printf("watcher adds monitor for: %s", path)
  92. }
  93. // Add the callback to global callback map.
  94. callbackIdMap.Set(callback.Id, callback)
  95. //intlog.Print("addWithCallbackFunc", name, path, callback.recursive)
  96. return
  97. }
  98. // Close closes the watcher.
  99. func (w *Watcher) Close() {
  100. w.events.Close()
  101. if err := w.watcher.Close(); err != nil {
  102. intlog.Error(err)
  103. }
  104. close(w.closeChan)
  105. }
  106. // Remove removes monitor and all callbacks associated with the <path> recursively.
  107. func (w *Watcher) Remove(path string) error {
  108. // Firstly remove the callbacks of the path.
  109. if r := w.callbacks.Remove(path); r != nil {
  110. list := r.(*glist.List)
  111. for {
  112. if r := list.PopFront(); r != nil {
  113. callbackIdMap.Remove(r.(*Callback).Id)
  114. } else {
  115. break
  116. }
  117. }
  118. }
  119. // Secondly remove monitor of all sub-files which have no callbacks.
  120. if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 {
  121. for _, subPath := range subPaths {
  122. if w.checkPathCanBeRemoved(subPath) {
  123. if err := w.watcher.Remove(subPath); err != nil {
  124. intlog.Error(err)
  125. }
  126. }
  127. }
  128. }
  129. // Lastly remove the monitor of the path from underlying monitor.
  130. return w.watcher.Remove(path)
  131. }
  132. // checkPathCanBeRemoved checks whether the given path have no callbacks bound.
  133. func (w *Watcher) checkPathCanBeRemoved(path string) bool {
  134. // Firstly check the callbacks in the watcher directly.
  135. if v := w.callbacks.Get(path); v != nil {
  136. return false
  137. }
  138. // Secondly check its parent whether has callbacks.
  139. dirPath := fileDir(path)
  140. if v := w.callbacks.Get(dirPath); v != nil {
  141. for _, c := range v.(*glist.List).FrontAll() {
  142. if c.(*Callback).recursive {
  143. return false
  144. }
  145. }
  146. return false
  147. }
  148. // Recursively check its parent.
  149. parentDirPath := ""
  150. for {
  151. parentDirPath = fileDir(dirPath)
  152. if parentDirPath == dirPath {
  153. break
  154. }
  155. if v := w.callbacks.Get(parentDirPath); v != nil {
  156. for _, c := range v.(*glist.List).FrontAll() {
  157. if c.(*Callback).recursive {
  158. return false
  159. }
  160. }
  161. return false
  162. }
  163. dirPath = parentDirPath
  164. }
  165. return true
  166. }
  167. // RemoveCallback removes callback with given callback id from watcher.
  168. func (w *Watcher) RemoveCallback(callbackId int) {
  169. callback := (*Callback)(nil)
  170. if r := callbackIdMap.Get(callbackId); r != nil {
  171. callback = r.(*Callback)
  172. }
  173. if callback != nil {
  174. if r := w.callbacks.Get(callback.Path); r != nil {
  175. r.(*glist.List).Remove(callback.elem)
  176. }
  177. callbackIdMap.Remove(callbackId)
  178. if callback.name != "" {
  179. w.nameSet.Remove(callback.name)
  180. }
  181. }
  182. }