github.com/wangyougui/gf/v2@v2.6.5/os/gfsnotify/gfsnotify.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/wangyougui/gf.
     6  
     7  // Package gfsnotify provides a platform-independent interface for file system notifications.
     8  package gfsnotify
     9  
    10  import (
    11  	"context"
    12  	"sync"
    13  	"time"
    14  
    15  	"github.com/fsnotify/fsnotify"
    16  
    17  	"github.com/wangyougui/gf/v2/container/glist"
    18  	"github.com/wangyougui/gf/v2/container/gmap"
    19  	"github.com/wangyougui/gf/v2/container/gqueue"
    20  	"github.com/wangyougui/gf/v2/container/gset"
    21  	"github.com/wangyougui/gf/v2/container/gtype"
    22  	"github.com/wangyougui/gf/v2/errors/gcode"
    23  	"github.com/wangyougui/gf/v2/errors/gerror"
    24  	"github.com/wangyougui/gf/v2/internal/intlog"
    25  	"github.com/wangyougui/gf/v2/os/gcache"
    26  )
    27  
    28  // Watcher is the monitor for file changes.
    29  type Watcher struct {
    30  	watcher   *fsnotify.Watcher // Underlying fsnotify object.
    31  	events    *gqueue.Queue     // Used for internal event management.
    32  	cache     *gcache.Cache     // Used for repeated event filter.
    33  	nameSet   *gset.StrSet      // Used for AddOnce feature.
    34  	callbacks *gmap.StrAnyMap   // Path(file/folder) to callbacks mapping.
    35  	closeChan chan struct{}     // Used for watcher closing notification.
    36  }
    37  
    38  // Callback is the callback function for Watcher.
    39  type Callback struct {
    40  	Id        int                // Unique id for callback object.
    41  	Func      func(event *Event) // Callback function.
    42  	Path      string             // Bound file path (absolute).
    43  	name      string             // Registered name for AddOnce.
    44  	elem      *glist.Element     // Element in the callbacks of watcher.
    45  	recursive bool               // Is bound to path recursively or not.
    46  }
    47  
    48  // Event is the event produced by underlying fsnotify.
    49  type Event struct {
    50  	event   fsnotify.Event // Underlying event.
    51  	Path    string         // Absolute file path.
    52  	Op      Op             // File operation.
    53  	Watcher *Watcher       // Parent watcher.
    54  }
    55  
    56  // Op is the bits union for file operations.
    57  type Op uint32
    58  
    59  // internalPanic is the custom panic for internal usage.
    60  type internalPanic string
    61  
    62  const (
    63  	CREATE Op = 1 << iota
    64  	WRITE
    65  	REMOVE
    66  	RENAME
    67  	CHMOD
    68  )
    69  
    70  const (
    71  	repeatEventFilterDuration               = time.Millisecond // Duration for repeated event filter.
    72  	callbackExitEventPanicStr internalPanic = "exit"           // Custom exit event for internal usage.
    73  )
    74  
    75  var (
    76  	mu                  sync.Mutex                // Mutex for concurrent safety of defaultWatcher.
    77  	defaultWatcher      *Watcher                  // Default watcher.
    78  	callbackIdMap       = gmap.NewIntAnyMap(true) // Id to callback mapping.
    79  	callbackIdGenerator = gtype.NewInt()          // Atomic id generator for callback.
    80  )
    81  
    82  // New creates and returns a new watcher.
    83  // Note that the watcher number is limited by the file handle setting of the system.
    84  // Eg: fs.inotify.max_user_instances system variable in linux systems.
    85  func New() (*Watcher, error) {
    86  	w := &Watcher{
    87  		cache:     gcache.New(),
    88  		events:    gqueue.New(),
    89  		nameSet:   gset.NewStrSet(true),
    90  		closeChan: make(chan struct{}),
    91  		callbacks: gmap.NewStrAnyMap(true),
    92  	}
    93  	if watcher, err := fsnotify.NewWatcher(); err == nil {
    94  		w.watcher = watcher
    95  	} else {
    96  		intlog.Printf(context.TODO(), "New watcher failed: %v", err)
    97  		return nil, err
    98  	}
    99  	w.watchLoop()
   100  	w.eventLoop()
   101  	return w, nil
   102  }
   103  
   104  // Add monitors `path` using default watcher with callback function `callbackFunc`.
   105  // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
   106  func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
   107  	w, err := getDefaultWatcher()
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  	return w.Add(path, callbackFunc, recursive...)
   112  }
   113  
   114  // AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`.
   115  // If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error
   116  // if it's called twice with the same `name`.
   117  //
   118  // The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
   119  func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
   120  	w, err := getDefaultWatcher()
   121  	if err != nil {
   122  		return nil, err
   123  	}
   124  	return w.AddOnce(name, path, callbackFunc, recursive...)
   125  }
   126  
   127  // Remove removes all monitoring callbacks of given `path` from watcher recursively.
   128  func Remove(path string) error {
   129  	w, err := getDefaultWatcher()
   130  	if err != nil {
   131  		return err
   132  	}
   133  	return w.Remove(path)
   134  }
   135  
   136  // RemoveCallback removes specified callback with given id from watcher.
   137  func RemoveCallback(callbackId int) error {
   138  	w, err := getDefaultWatcher()
   139  	if err != nil {
   140  		return err
   141  	}
   142  	callback := (*Callback)(nil)
   143  	if r := callbackIdMap.Get(callbackId); r != nil {
   144  		callback = r.(*Callback)
   145  	}
   146  	if callback == nil {
   147  		return gerror.NewCodef(gcode.CodeInvalidParameter, `callback for id %d not found`, callbackId)
   148  	}
   149  	w.RemoveCallback(callbackId)
   150  	return nil
   151  }
   152  
   153  // Exit is only used in the callback function, which can be used to remove current callback
   154  // of itself from the watcher.
   155  func Exit() {
   156  	panic(callbackExitEventPanicStr)
   157  }
   158  
   159  // getDefaultWatcher creates and returns the default watcher.
   160  // This is used for lazy initialization purpose.
   161  func getDefaultWatcher() (*Watcher, error) {
   162  	mu.Lock()
   163  	defer mu.Unlock()
   164  	if defaultWatcher != nil {
   165  		return defaultWatcher, nil
   166  	}
   167  	var err error
   168  	defaultWatcher, err = New()
   169  	return defaultWatcher, err
   170  }