github.com/gogf/gf@v1.16.9/os/gfsnotify/gfsnotify_watcher_loop.go (about)

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