github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/go/filemonitor/filemonitor.go (about)

     1  package filemonitor
     2  
     3  import (
     4  	"sync"
     5  	"time"
     6  )
     7  
     8  const DefaultFileChangeDelay = 300 * time.Millisecond
     9  
    10  type FileMonitor interface {
    11  	Listen() <-chan []string
    12  	Add(string) error
    13  	Close() error
    14  }
    15  
    16  type fileMonitor struct {
    17  	listeners     []chan []string
    18  	listenerMutex sync.Mutex
    19  }
    20  
    21  func (f *fileMonitor) Listen() <-chan []string {
    22  	f.listenerMutex.Lock()
    23  	defer f.listenerMutex.Unlock()
    24  
    25  	c := make(chan []string)
    26  	f.listeners = append(f.listeners, c)
    27  
    28  	return c
    29  }
    30  
    31  type gatheringMonitor struct {
    32  	fileMonitor
    33  	changes         chan string
    34  	fileChangeDelay time.Duration
    35  }
    36  
    37  // Create the changes channel and serve debounced changes to listeners.
    38  // The changes channel must be created before this is started.
    39  // Closing the changes channel causes this to close all listener
    40  // channels and return.
    41  func (f *gatheringMonitor) serveListeners() {
    42  	never := make(<-chan time.Time)
    43  	deadline := never
    44  
    45  	collected := make(map[string]bool, 1)
    46  	for {
    47  		select {
    48  		case change := <-f.changes:
    49  			// Channel closed
    50  			if change == "" {
    51  				f.listenerMutex.Lock()
    52  				defer f.listenerMutex.Unlock()
    53  
    54  				for _, listener := range f.listeners {
    55  					close(listener)
    56  				}
    57  				return
    58  			}
    59  
    60  			collected[change] = true
    61  			if deadline == never {
    62  				deadline = time.After(f.fileChangeDelay)
    63  			}
    64  		case <-deadline:
    65  			list := make([]string, 0, len(collected))
    66  			for f := range collected {
    67  				list = append(list, f)
    68  			}
    69  
    70  			for _, l := range f.listeners {
    71  				l <- list
    72  			}
    73  
    74  			deadline = never
    75  			collected = make(map[string]bool, 1)
    76  		}
    77  	}
    78  }