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