batcher.go (2149B)
1 // Copyright 2020 The Hugo Authors. All rights reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package watcher 15 16 import ( 17 "time" 18 19 "github.com/fsnotify/fsnotify" 20 "github.com/gohugoio/hugo/watcher/filenotify" 21 ) 22 23 // Batcher batches file watch events in a given interval. 24 type Batcher struct { 25 filenotify.FileWatcher 26 interval time.Duration 27 done chan struct{} 28 29 Events chan []fsnotify.Event // Events are returned on this channel 30 } 31 32 // New creates and starts a Batcher with the given time interval. 33 // It will fall back to a poll based watcher if native isn's supported. 34 // To always use polling, set poll to true. 35 func New(intervalBatcher, intervalPoll time.Duration, poll bool) (*Batcher, error) { 36 var err error 37 var watcher filenotify.FileWatcher 38 39 if poll { 40 watcher = filenotify.NewPollingWatcher(intervalPoll) 41 } else { 42 watcher, err = filenotify.New(intervalPoll) 43 } 44 45 if err != nil { 46 return nil, err 47 } 48 49 batcher := &Batcher{} 50 batcher.FileWatcher = watcher 51 batcher.interval = intervalBatcher 52 batcher.done = make(chan struct{}, 1) 53 batcher.Events = make(chan []fsnotify.Event, 1) 54 55 if err == nil { 56 go batcher.run() 57 } 58 59 return batcher, nil 60 } 61 62 func (b *Batcher) run() { 63 tick := time.Tick(b.interval) 64 evs := make([]fsnotify.Event, 0) 65 OuterLoop: 66 for { 67 select { 68 case ev := <-b.FileWatcher.Events(): 69 evs = append(evs, ev) 70 case <-tick: 71 if len(evs) == 0 { 72 continue 73 } 74 b.Events <- evs 75 evs = make([]fsnotify.Event, 0) 76 case <-b.done: 77 break OuterLoop 78 } 79 } 80 close(b.done) 81 } 82 83 // Close stops the watching of the files. 84 func (b *Batcher) Close() { 85 b.done <- struct{}{} 86 b.FileWatcher.Close() 87 }