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 }