gfsnotify_watcher_loop.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  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
  7. import (
  8. "context"
  9. "github.com/gogf/gf/container/glist"
  10. "github.com/gogf/gf/internal/intlog"
  11. )
  12. // watchLoop starts the loop for event listening from underlying inotify monitor.
  13. func (w *Watcher) watchLoop() {
  14. go func() {
  15. for {
  16. select {
  17. // Close event.
  18. case <-w.closeChan:
  19. return
  20. // Event listening.
  21. case ev := <-w.watcher.Events:
  22. // Filter the repeated event in custom duration.
  23. w.cache.SetIfNotExist(ev.String(), func() (interface{}, error) {
  24. w.events.Push(&Event{
  25. event: ev,
  26. Path: ev.Name,
  27. Op: Op(ev.Op),
  28. Watcher: w,
  29. })
  30. return struct{}{}, nil
  31. }, repeatEventFilterDuration)
  32. case err := <-w.watcher.Errors:
  33. intlog.Error(context.TODO(), err)
  34. }
  35. }
  36. }()
  37. }
  38. // eventLoop is the core event handler.
  39. func (w *Watcher) eventLoop() {
  40. go func() {
  41. for {
  42. if v := w.events.Pop(); v != nil {
  43. event := v.(*Event)
  44. // If there's no any callback of this path, it removes it from monitor.
  45. callbacks := w.getCallbacks(event.Path)
  46. if len(callbacks) == 0 {
  47. w.watcher.Remove(event.Path)
  48. continue
  49. }
  50. switch {
  51. case event.IsRemove():
  52. // It should check again the existence of the path.
  53. // It adds it back to the monitor if it still exists.
  54. if fileExists(event.Path) {
  55. // It adds the path back to monitor.
  56. // We need no worry about the repeat adding.
  57. if err := w.watcher.Add(event.Path); err != nil {
  58. intlog.Error(context.TODO(), err)
  59. } else {
  60. intlog.Printf(context.TODO(), "fake remove event, watcher re-adds monitor for: %s", event.Path)
  61. }
  62. // Change the event to RENAME, which means it renames itself to its origin name.
  63. event.Op = RENAME
  64. }
  65. case event.IsRename():
  66. // It should check again the existence of the path.
  67. // It adds it back to the monitor if it still exists.
  68. // Especially Some editors might do RENAME and then CHMOD when it's editing file.
  69. if fileExists(event.Path) {
  70. // It might lost the monitoring for the path, so we add the path back to monitor.
  71. // We need no worry about the repeat adding.
  72. if err := w.watcher.Add(event.Path); err != nil {
  73. intlog.Error(context.TODO(), err)
  74. } else {
  75. intlog.Printf(context.TODO(), "fake rename event, watcher re-adds monitor for: %s", event.Path)
  76. }
  77. // Change the event to CHMOD.
  78. event.Op = CHMOD
  79. }
  80. case event.IsCreate():
  81. // =========================================
  82. // Note that it here just adds the path to monitor without any callback registering,
  83. // because its parent already has the callbacks.
  84. // =========================================
  85. if fileIsDir(event.Path) {
  86. // If it's a folder, it then does adding recursively to monitor.
  87. for _, subPath := range fileAllDirs(event.Path) {
  88. if fileIsDir(subPath) {
  89. if err := w.watcher.Add(subPath); err != nil {
  90. intlog.Error(context.TODO(), err)
  91. } else {
  92. intlog.Printf(context.TODO(), "folder creation event, watcher adds monitor for: %s", subPath)
  93. }
  94. }
  95. }
  96. } else {
  97. // If it's a file, it directly adds it to monitor.
  98. if err := w.watcher.Add(event.Path); err != nil {
  99. intlog.Error(context.TODO(), err)
  100. } else {
  101. intlog.Printf(context.TODO(), "file creation event, watcher adds monitor for: %s", event.Path)
  102. }
  103. }
  104. }
  105. // Calling the callbacks in order.
  106. for _, v := range callbacks {
  107. go func(callback *Callback) {
  108. defer func() {
  109. if err := recover(); err != nil {
  110. switch err {
  111. case callbackExitEventPanicStr:
  112. w.RemoveCallback(callback.Id)
  113. default:
  114. panic(err)
  115. }
  116. }
  117. }()
  118. callback.Func(event)
  119. }(v)
  120. }
  121. } else {
  122. break
  123. }
  124. }
  125. }()
  126. }
  127. // getCallbacks searches and returns all callbacks with given `path`.
  128. // It also searches its parents for callbacks if they're recursive.
  129. func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) {
  130. // Firstly add the callbacks of itself.
  131. if v := w.callbacks.Get(path); v != nil {
  132. for _, v := range v.(*glist.List).FrontAll() {
  133. callback := v.(*Callback)
  134. callbacks = append(callbacks, callback)
  135. }
  136. }
  137. // Secondly searches its direct parent for callbacks.
  138. // It is special handling here, which is the different between `recursive` and `not recursive` logic
  139. // for direct parent folder of `path` that events are from.
  140. dirPath := fileDir(path)
  141. if v := w.callbacks.Get(dirPath); v != nil {
  142. for _, v := range v.(*glist.List).FrontAll() {
  143. callback := v.(*Callback)
  144. callbacks = append(callbacks, callback)
  145. }
  146. }
  147. // Lastly searches all the parents of directory of `path` recursively for callbacks.
  148. for {
  149. parentDirPath := fileDir(dirPath)
  150. if parentDirPath == dirPath {
  151. break
  152. }
  153. if v := w.callbacks.Get(parentDirPath); v != nil {
  154. for _, v := range v.(*glist.List).FrontAll() {
  155. callback := v.(*Callback)
  156. if callback.recursive {
  157. callbacks = append(callbacks, callback)
  158. }
  159. }
  160. }
  161. dirPath = parentDirPath
  162. }
  163. return
  164. }