github.com/zhouyu0/docker-note@v0.0.0-20190722021225-b8d3825084db/pkg/filenotify/poller.go (about)

     1  package filenotify // import "github.com/docker/docker/pkg/filenotify"
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"os"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/sirupsen/logrus"
    11  
    12  	"github.com/fsnotify/fsnotify"
    13  )
    14  
    15  var (
    16  	// errPollerClosed is returned when the poller is closed
    17  	errPollerClosed = errors.New("poller is closed")
    18  	// errNoSuchWatch is returned when trying to remove a watch that doesn't exist
    19  	errNoSuchWatch = errors.New("watch does not exist")
    20  )
    21  
    22  // watchWaitTime is the time to wait between file poll loops
    23  const watchWaitTime = 200 * time.Millisecond
    24  
    25  // filePoller is used to poll files for changes, especially in cases where fsnotify
    26  // can't be run (e.g. when inotify handles are exhausted)
    27  // filePoller satisfies the FileWatcher interface
    28  type filePoller struct {
    29  	// watches is the list of files currently being polled, close the associated channel to stop the watch
    30  	watches map[string]chan struct{}
    31  	// events is the channel to listen to for watch events
    32  	events chan fsnotify.Event
    33  	// errors is the channel to listen to for watch errors
    34  	errors chan error
    35  	// mu locks the poller for modification
    36  	mu sync.Mutex
    37  	// closed is used to specify when the poller has already closed
    38  	closed bool
    39  }
    40  
    41  // Add adds a filename to the list of watches
    42  // once added the file is polled for changes in a separate goroutine
    43  func (w *filePoller) Add(name string) error {
    44  	w.mu.Lock()
    45  	defer w.mu.Unlock()
    46  
    47  	if w.closed {
    48  		return errPollerClosed
    49  	}
    50  
    51  	f, err := os.Open(name)
    52  	if err != nil {
    53  		return err
    54  	}
    55  	fi, err := os.Stat(name)
    56  	if err != nil {
    57  		f.Close()
    58  		return err
    59  	}
    60  
    61  	if w.watches == nil {
    62  		w.watches = make(map[string]chan struct{})
    63  	}
    64  	if _, exists := w.watches[name]; exists {
    65  		f.Close()
    66  		return fmt.Errorf("watch exists")
    67  	}
    68  	chClose := make(chan struct{})
    69  	w.watches[name] = chClose
    70  
    71  	go w.watch(f, fi, chClose)
    72  	return nil
    73  }
    74  
    75  // Remove stops and removes watch with the specified name
    76  func (w *filePoller) Remove(name string) error {
    77  	w.mu.Lock()
    78  	defer w.mu.Unlock()
    79  	return w.remove(name)
    80  }
    81  
    82  func (w *filePoller) remove(name string) error {
    83  	if w.closed {
    84  		return errPollerClosed
    85  	}
    86  
    87  	chClose, exists := w.watches[name]
    88  	if !exists {
    89  		return errNoSuchWatch
    90  	}
    91  	close(chClose)
    92  	delete(w.watches, name)
    93  	return nil
    94  }
    95  
    96  // Events returns the event channel
    97  // This is used for notifications on events about watched files
    98  func (w *filePoller) Events() <-chan fsnotify.Event {
    99  	return w.events
   100  }
   101  
   102  // Errors returns the errors channel
   103  // This is used for notifications about errors on watched files
   104  func (w *filePoller) Errors() <-chan error {
   105  	return w.errors
   106  }
   107  
   108  // Close closes the poller
   109  // All watches are stopped, removed, and the poller cannot be added to
   110  func (w *filePoller) Close() error {
   111  	w.mu.Lock()
   112  	defer w.mu.Unlock()
   113  
   114  	if w.closed {
   115  		return nil
   116  	}
   117  
   118  	for name := range w.watches {
   119  		w.remove(name)
   120  	}
   121  	w.closed = true
   122  	return nil
   123  }
   124  
   125  // sendEvent publishes the specified event to the events channel
   126  func (w *filePoller) sendEvent(e fsnotify.Event, chClose <-chan struct{}) error {
   127  	select {
   128  	case w.events <- e:
   129  	case <-chClose:
   130  		return fmt.Errorf("closed")
   131  	}
   132  	return nil
   133  }
   134  
   135  // sendErr publishes the specified error to the errors channel
   136  func (w *filePoller) sendErr(e error, chClose <-chan struct{}) error {
   137  	select {
   138  	case w.errors <- e:
   139  	case <-chClose:
   140  		return fmt.Errorf("closed")
   141  	}
   142  	return nil
   143  }
   144  
   145  // watch is responsible for polling the specified file for changes
   146  // upon finding changes to a file or errors, sendEvent/sendErr is called
   147  func (w *filePoller) watch(f *os.File, lastFi os.FileInfo, chClose chan struct{}) {
   148  	defer f.Close()
   149  	for {
   150  		select {
   151  		case <-time.After(watchWaitTime):
   152  		case <-chClose:
   153  			logrus.Debugf("watch for %s closed", f.Name())
   154  			return
   155  		}
   156  
   157  		fi, err := os.Stat(f.Name())
   158  		if err != nil {
   159  			// if we got an error here and lastFi is not set, we can presume that nothing has changed
   160  			// This should be safe since before `watch()` is called, a stat is performed, there is any error `watch` is not called
   161  			if lastFi == nil {
   162  				continue
   163  			}
   164  			// If it doesn't exist at this point, it must have been removed
   165  			// no need to send the error here since this is a valid operation
   166  			if os.IsNotExist(err) {
   167  				if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Remove, Name: f.Name()}, chClose); err != nil {
   168  					return
   169  				}
   170  				lastFi = nil
   171  				continue
   172  			}
   173  			// at this point, send the error
   174  			if err := w.sendErr(err, chClose); err != nil {
   175  				return
   176  			}
   177  			continue
   178  		}
   179  
   180  		if lastFi == nil {
   181  			if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Create, Name: fi.Name()}, chClose); err != nil {
   182  				return
   183  			}
   184  			lastFi = fi
   185  			continue
   186  		}
   187  
   188  		if fi.Mode() != lastFi.Mode() {
   189  			if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Chmod, Name: fi.Name()}, chClose); err != nil {
   190  				return
   191  			}
   192  			lastFi = fi
   193  			continue
   194  		}
   195  
   196  		if fi.ModTime() != lastFi.ModTime() || fi.Size() != lastFi.Size() {
   197  			if err := w.sendEvent(fsnotify.Event{Op: fsnotify.Write, Name: fi.Name()}, chClose); err != nil {
   198  				return
   199  			}
   200  			lastFi = fi
   201  			continue
   202  		}
   203  	}
   204  }