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 }