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