github.com/zhongdalu/gf@v1.0.0/g/os/gfsnotify/gfsnotify_watcher.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). 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/zhongdalu/gf.
     6  
     7  package gfsnotify
     8  
     9  import (
    10  	"errors"
    11  	"fmt"
    12  
    13  	"github.com/zhongdalu/gf/g/container/glist"
    14  )
    15  
    16  // 添加监控,path参数支持文件或者目录路径,recursive为非必需参数,默认为递归监控(当path为目录时)。
    17  // 如果添加目录,这里只会返回目录的callback,按照callback删除时会递归删除。
    18  func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
    19  	// 首先添加这个文件/目录
    20  	callback, err = w.addWithCallbackFunc(path, callbackFunc, recursive...)
    21  	if err != nil {
    22  		return nil, err
    23  	}
    24  	// 如果需要递归,那么递归添加其下的子级目录,
    25  	// 注意!!
    26  	// 1、这里只递归添加**目录**, 而非文件,因为监控了目录即监控了其下一级的文件;
    27  	// 2、这里只是添加底层监控对象对**子级所有目录**的监控,没有任何回调函数的设置,在事件产生时会回溯查找父级的回调函数;
    28  	if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) {
    29  		for _, subPath := range fileAllDirs(path) {
    30  			if fileIsDir(subPath) {
    31  				w.watcher.Add(subPath)
    32  			}
    33  		}
    34  	}
    35  	return
    36  }
    37  
    38  // 添加对指定文件/目录的监听,并给定回调函数
    39  func (w *Watcher) addWithCallbackFunc(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
    40  	// 这里统一转换为当前系统的绝对路径,便于统一监控文件名称
    41  	if t := fileRealPath(path); t == "" {
    42  		return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path))
    43  	} else {
    44  		path = t
    45  	}
    46  	callback = &Callback{
    47  		Id:        callbackIdGenerator.Add(1),
    48  		Func:      callbackFunc,
    49  		Path:      path,
    50  		recursive: true,
    51  	}
    52  	if len(recursive) > 0 {
    53  		callback.recursive = recursive[0]
    54  	}
    55  	// 注册回调函数
    56  	w.callbacks.LockFunc(func(m map[string]interface{}) {
    57  		list := (*glist.List)(nil)
    58  		if v, ok := m[path]; !ok {
    59  			list = glist.New()
    60  			m[path] = list
    61  		} else {
    62  			list = v.(*glist.List)
    63  		}
    64  		callback.elem = list.PushBack(callback)
    65  	})
    66  	// 添加底层监听
    67  	w.watcher.Add(path)
    68  	// 添加成功后会注册该callback id到全局的哈希表
    69  	callbackIdMap.Set(callback.Id, callback)
    70  	return
    71  }
    72  
    73  // 关闭监听管理对象
    74  func (w *Watcher) Close() {
    75  	w.events.Close()
    76  	w.watcher.Close()
    77  	close(w.closeChan)
    78  }
    79  
    80  // 递归移除对指定文件/目录的所有监听回调
    81  func (w *Watcher) Remove(path string) error {
    82  	// 首先移除path注册的回调注册,以及callbackIdMap中的ID
    83  	if r := w.callbacks.Remove(path); r != nil {
    84  		list := r.(*glist.List)
    85  		for {
    86  			if r := list.PopFront(); r != nil {
    87  				callbackIdMap.Remove(r.(*Callback).Id)
    88  			} else {
    89  				break
    90  			}
    91  		}
    92  	}
    93  	// 其次递归判断所有的子级是否可删除监听
    94  	if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 {
    95  		for _, subPath := range subPaths {
    96  			if w.checkPathCanBeRemoved(subPath) {
    97  				w.watcher.Remove(subPath)
    98  			}
    99  		}
   100  	}
   101  	// 最后移除底层的监听
   102  	return w.watcher.Remove(path)
   103  }
   104  
   105  // 判断给定的路径是否可以删除监听(只有所有回调函数都没有了才能删除)
   106  func (w *Watcher) checkPathCanBeRemoved(path string) bool {
   107  	// 首先检索path对应的回调函数
   108  	if v := w.callbacks.Get(path); v != nil {
   109  		return false
   110  	}
   111  	// 其次查找父级目录有无回调注册
   112  	dirPath := fileDir(path)
   113  	if v := w.callbacks.Get(dirPath); v != nil {
   114  		return false
   115  	}
   116  	// 最后回溯查找递归回调函数
   117  	for {
   118  		parentDirPath := fileDir(dirPath)
   119  		if parentDirPath == dirPath {
   120  			break
   121  		}
   122  		if v := w.callbacks.Get(parentDirPath); v != nil {
   123  			return false
   124  		}
   125  		dirPath = parentDirPath
   126  	}
   127  	return true
   128  }
   129  
   130  // 根据指定的回调函数ID,移出指定的inotify回调函数
   131  func (w *Watcher) RemoveCallback(callbackId int) {
   132  	callback := (*Callback)(nil)
   133  	if r := callbackIdMap.Get(callbackId); r != nil {
   134  		callback = r.(*Callback)
   135  	}
   136  	if callback != nil {
   137  		if r := w.callbacks.Get(callback.Path); r != nil {
   138  			r.(*glist.List).Remove(callback.elem)
   139  		}
   140  		callbackIdMap.Remove(callbackId)
   141  	}
   142  }