gf/os/gfsnotify/gfsnotify_watcher.go

194 lines
5.7 KiB
Go
Raw Normal View History

// Copyright 2018 gf Author(https://github.com/gogf/gf). 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 (
2019-06-19 09:06:52 +08:00
"errors"
"fmt"
2019-10-31 23:37:33 +08:00
"github.com/gogf/gf/internal/intlog"
2019-07-02 23:57:49 +08:00
2019-07-29 21:01:19 +08:00
"github.com/gogf/gf/container/glist"
)
2019-10-31 23:37:33 +08:00
// Add monitors <path> with callback function <callbackFunc> to the watcher.
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
// which is true in default.
2019-06-19 09:06:52 +08:00
func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
2019-10-31 23:37:33 +08:00
return w.AddOnce("", path, callbackFunc, recursive...)
}
// AddOnce monitors <path> with callback function <callbackFunc> only once using unique name
// <name> to the watcher. If AddOnce is called multiple times with the same <name> parameter,
// <path> is only added to monitor once.
// It returns error if it's called twice with the same <name>.
2019-10-31 23:37:33 +08:00
//
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
// which is true in default.
2019-10-31 23:37:33 +08:00
func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
2020-04-13 23:44:43 +08:00
w.nameSet.AddIfNotExistFuncLock(name, func() bool {
2019-10-31 23:37:33 +08:00
// Firstly add the path to watcher.
callback, err = w.addWithCallbackFunc(name, path, callbackFunc, recursive...)
if err != nil {
2020-04-13 23:44:43 +08:00
return false
2019-10-31 23:37:33 +08:00
}
// If it's recursive adding, it then adds all sub-folders to the monitor.
// NOTE:
// 1. It only recursively adds **folders** to the monitor, NOT files,
// because if the folders are monitored and their sub-files are also monitored.
// 2. It bounds no callbacks to the folders, because it will search the callbacks
// from its parent recursively if any event produced.
if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) {
for _, subPath := range fileAllDirs(path) {
if fileIsDir(subPath) {
if err := w.watcher.Add(subPath); err != nil {
intlog.Error(err)
} else {
intlog.Printf("watcher adds monitor for: %s", subPath)
2019-10-31 23:37:33 +08:00
}
}
2019-06-19 09:06:52 +08:00
}
}
if name == "" {
return false
}
2020-04-13 23:44:43 +08:00
return true
2019-10-31 23:37:33 +08:00
})
2019-06-19 09:06:52 +08:00
return
}
2019-10-31 23:37:33 +08:00
// addWithCallbackFunc adds the path to underlying monitor, creates and returns a callback object.
func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
// Check and convert the given path to absolute path.
2019-06-19 09:06:52 +08:00
if t := fileRealPath(path); t == "" {
return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
} else {
path = t
}
2019-10-31 23:37:33 +08:00
// Create callback object.
2019-06-19 09:06:52 +08:00
callback = &Callback{
Id: callbackIdGenerator.Add(1),
Func: callbackFunc,
Path: path,
2019-10-31 23:37:33 +08:00
name: name,
recursive: true,
2019-06-19 09:06:52 +08:00
}
if len(recursive) > 0 {
callback.recursive = recursive[0]
}
2019-10-31 23:37:33 +08:00
// Register the callback to watcher.
2019-06-19 09:06:52 +08:00
w.callbacks.LockFunc(func(m map[string]interface{}) {
list := (*glist.List)(nil)
if v, ok := m[path]; !ok {
list = glist.New(true)
2019-06-19 09:06:52 +08:00
m[path] = list
} else {
list = v.(*glist.List)
}
callback.elem = list.PushBack(callback)
})
2019-10-31 23:37:33 +08:00
// Add the path to underlying monitor.
if err := w.watcher.Add(path); err != nil {
intlog.Error(err)
} else {
intlog.Printf("watcher adds monitor for: %s", path)
2019-10-31 23:37:33 +08:00
}
// Add the callback to global callback map.
2019-06-19 09:06:52 +08:00
callbackIdMap.Set(callback.Id, callback)
2019-10-31 23:37:33 +08:00
2019-11-01 15:31:26 +08:00
//intlog.Print("addWithCallbackFunc", name, path, callback.recursive)
2019-06-19 09:06:52 +08:00
return
}
2019-10-31 23:37:33 +08:00
// Close closes the watcher.
2018-11-16 18:20:09 +08:00
func (w *Watcher) Close() {
2019-06-19 09:06:52 +08:00
w.events.Close()
2019-10-31 23:37:33 +08:00
if err := w.watcher.Close(); err != nil {
intlog.Error(err)
}
2019-06-19 09:06:52 +08:00
close(w.closeChan)
}
2019-10-31 23:37:33 +08:00
// Remove removes monitor and all callbacks associated with the <path> recursively.
func (w *Watcher) Remove(path string) error {
2019-10-31 23:37:33 +08:00
// Firstly remove the callbacks of the path.
2019-06-19 09:06:52 +08:00
if r := w.callbacks.Remove(path); r != nil {
list := r.(*glist.List)
for {
if r := list.PopFront(); r != nil {
callbackIdMap.Remove(r.(*Callback).Id)
} else {
break
}
}
}
2019-10-31 23:37:33 +08:00
// Secondly remove monitor of all sub-files which have no callbacks.
2019-06-19 09:06:52 +08:00
if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 {
for _, subPath := range subPaths {
if w.checkPathCanBeRemoved(subPath) {
2019-10-31 23:37:33 +08:00
if err := w.watcher.Remove(subPath); err != nil {
intlog.Error(err)
}
2019-06-19 09:06:52 +08:00
}
}
}
2019-10-31 23:37:33 +08:00
// Lastly remove the monitor of the path from underlying monitor.
2019-06-19 09:06:52 +08:00
return w.watcher.Remove(path)
}
2019-10-31 23:37:33 +08:00
// checkPathCanBeRemoved checks whether the given path have no callbacks bound.
func (w *Watcher) checkPathCanBeRemoved(path string) bool {
2019-10-31 23:37:33 +08:00
// Firstly check the callbacks in the watcher directly.
2019-06-19 09:06:52 +08:00
if v := w.callbacks.Get(path); v != nil {
return false
}
2019-10-31 23:37:33 +08:00
// Secondly check its parent whether has callbacks.
2019-06-19 09:06:52 +08:00
dirPath := fileDir(path)
if v := w.callbacks.Get(dirPath); v != nil {
for _, c := range v.(*glist.List).FrontAll() {
if c.(*Callback).recursive {
return false
}
}
2019-06-19 09:06:52 +08:00
return false
}
2019-10-31 23:37:33 +08:00
// Recursively check its parent.
parentDirPath := ""
2019-06-19 09:06:52 +08:00
for {
2019-10-31 23:37:33 +08:00
parentDirPath = fileDir(dirPath)
2019-06-19 09:06:52 +08:00
if parentDirPath == dirPath {
break
}
if v := w.callbacks.Get(parentDirPath); v != nil {
for _, c := range v.(*glist.List).FrontAll() {
if c.(*Callback).recursive {
return false
}
}
2019-06-19 09:06:52 +08:00
return false
}
dirPath = parentDirPath
}
return true
}
2019-10-31 23:37:33 +08:00
// RemoveCallback removes callback with given callback id from watcher.
func (w *Watcher) RemoveCallback(callbackId int) {
2019-06-19 09:06:52 +08:00
callback := (*Callback)(nil)
if r := callbackIdMap.Get(callbackId); r != nil {
callback = r.(*Callback)
}
if callback != nil {
if r := w.callbacks.Get(callback.Path); r != nil {
r.(*glist.List).Remove(callback.elem)
}
callbackIdMap.Remove(callbackId)
2019-10-31 23:37:33 +08:00
if callback.name != "" {
w.nameSet.Remove(callback.name)
}
2019-06-19 09:06:52 +08:00
}
}