123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174 |
- // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- package gfsnotify
- import (
- "context"
- "github.com/gogf/gf/container/glist"
- "github.com/gogf/gf/internal/intlog"
- )
- // watchLoop starts the loop for event listening from underlying inotify monitor.
- func (w *Watcher) watchLoop() {
- go func() {
- for {
- select {
- // Close event.
- case <-w.closeChan:
- return
- // Event listening.
- case ev := <-w.watcher.Events:
- // Filter the repeated event in custom duration.
- w.cache.SetIfNotExist(ev.String(), func() (interface{}, error) {
- w.events.Push(&Event{
- event: ev,
- Path: ev.Name,
- Op: Op(ev.Op),
- Watcher: w,
- })
- return struct{}{}, nil
- }, repeatEventFilterDuration)
- case err := <-w.watcher.Errors:
- intlog.Error(context.TODO(), err)
- }
- }
- }()
- }
- // eventLoop is the core event handler.
- func (w *Watcher) eventLoop() {
- go func() {
- for {
- if v := w.events.Pop(); v != nil {
- event := v.(*Event)
- // If there's no any callback of this path, it removes it from monitor.
- callbacks := w.getCallbacks(event.Path)
- if len(callbacks) == 0 {
- w.watcher.Remove(event.Path)
- continue
- }
- switch {
- case event.IsRemove():
- // It should check again the existence of the path.
- // It adds it back to the monitor if it still exists.
- if fileExists(event.Path) {
- // It adds the path back to monitor.
- // We need no worry about the repeat adding.
- if err := w.watcher.Add(event.Path); err != nil {
- intlog.Error(context.TODO(), err)
- } else {
- intlog.Printf(context.TODO(), "fake remove event, watcher re-adds monitor for: %s", event.Path)
- }
- // Change the event to RENAME, which means it renames itself to its origin name.
- event.Op = RENAME
- }
- case event.IsRename():
- // It should check again the existence of the path.
- // It adds it back to the monitor if it still exists.
- // Especially Some editors might do RENAME and then CHMOD when it's editing file.
- if fileExists(event.Path) {
- // It might lost the monitoring for the path, so we add the path back to monitor.
- // We need no worry about the repeat adding.
- if err := w.watcher.Add(event.Path); err != nil {
- intlog.Error(context.TODO(), err)
- } else {
- intlog.Printf(context.TODO(), "fake rename event, watcher re-adds monitor for: %s", event.Path)
- }
- // Change the event to CHMOD.
- event.Op = CHMOD
- }
- case event.IsCreate():
- // =========================================
- // Note that it here just adds the path to monitor without any callback registering,
- // because its parent already has the callbacks.
- // =========================================
- if fileIsDir(event.Path) {
- // If it's a folder, it then does adding recursively to monitor.
- for _, subPath := range fileAllDirs(event.Path) {
- if fileIsDir(subPath) {
- if err := w.watcher.Add(subPath); err != nil {
- intlog.Error(context.TODO(), err)
- } else {
- intlog.Printf(context.TODO(), "folder creation event, watcher adds monitor for: %s", subPath)
- }
- }
- }
- } else {
- // If it's a file, it directly adds it to monitor.
- if err := w.watcher.Add(event.Path); err != nil {
- intlog.Error(context.TODO(), err)
- } else {
- intlog.Printf(context.TODO(), "file creation event, watcher adds monitor for: %s", event.Path)
- }
- }
- }
- // Calling the callbacks in order.
- for _, v := range callbacks {
- go func(callback *Callback) {
- defer func() {
- if err := recover(); err != nil {
- switch err {
- case callbackExitEventPanicStr:
- w.RemoveCallback(callback.Id)
- default:
- panic(err)
- }
- }
- }()
- callback.Func(event)
- }(v)
- }
- } else {
- break
- }
- }
- }()
- }
- // getCallbacks searches and returns all callbacks with given `path`.
- // It also searches its parents for callbacks if they're recursive.
- func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) {
- // Firstly add the callbacks of itself.
- if v := w.callbacks.Get(path); v != nil {
- for _, v := range v.(*glist.List).FrontAll() {
- callback := v.(*Callback)
- callbacks = append(callbacks, callback)
- }
- }
- // Secondly searches its direct parent for callbacks.
- // It is special handling here, which is the different between `recursive` and `not recursive` logic
- // for direct parent folder of `path` that events are from.
- dirPath := fileDir(path)
- if v := w.callbacks.Get(dirPath); v != nil {
- for _, v := range v.(*glist.List).FrontAll() {
- callback := v.(*Callback)
- callbacks = append(callbacks, callback)
- }
- }
- // Lastly searches all the parents of directory of `path` recursively for callbacks.
- for {
- parentDirPath := fileDir(dirPath)
- if parentDirPath == dirPath {
- break
- }
- if v := w.callbacks.Get(parentDirPath); v != nil {
- for _, v := range v.(*glist.List).FrontAll() {
- callback := v.(*Callback)
- if callback.recursive {
- callbacks = append(callbacks, callback)
- }
- }
- }
- dirPath = parentDirPath
- }
- return
- }
|