@@ -10,29 +10,27 @@ import (
1010)
1111
1212type watch struct {
13- watcher * fsnotify.Watcher
14- events <- chan fsnotify.Event
15- quit chan struct {}
16- loads map [string ]bool
17- loadTimer * time.Timer
18- updates map [string ]bool
19- updateTimer * time.Timer
20- dirChan chan <- * dir
21- fileChan chan <- * file
22- delChan chan <- string
13+ watcher * fsnotify.Watcher
14+ events <- chan fsnotify.Event
15+ quit chan struct {}
16+ pending map [watchUpdate ]bool
17+ timeout chan watchUpdate
18+ dirChan chan <- * dir
19+ fileChan chan <- * file
20+ delChan chan <- string
2321}
2422
2523func newWatch (dirChan chan <- * dir , fileChan chan <- * file , delChan chan <- string ) * watch {
26- return & watch {
27- quit : make (chan struct {}),
28- loads : make (map [string ]bool ),
29- loadTimer : time .NewTimer (0 ),
30- updates : make (map [string ]bool ),
31- updateTimer : time .NewTimer (0 ),
32- dirChan : dirChan ,
33- fileChan : fileChan ,
34- delChan : delChan ,
24+ watch := & watch {
25+ quit : make (chan struct {}),
26+ pending : make (map [watchUpdate ]bool ),
27+ timeout : make (chan watchUpdate , 1024 ),
28+ dirChan : dirChan ,
29+ fileChan : fileChan ,
30+ delChan : delChan ,
3531 }
32+
33+ return watch
3634}
3735
3836func (watch * watch ) start () {
@@ -83,17 +81,17 @@ func (watch *watch) loop() {
8381 case ev := <- watch .events :
8482 if ev .Has (fsnotify .Create ) {
8583 for _ , path := range watch .getSameDirs (filepath .Dir (ev .Name )) {
86- watch .addLoad ( path )
87- watch .addUpdate (path )
84+ watch .addUpdate ( watchUpdate { "dir" , path } )
85+ watch .addUpdate (watchUpdate { "file" , path } )
8886 }
8987 }
9088
9189 if ev .Has (fsnotify .Remove ) || ev .Has (fsnotify .Rename ) {
9290 dir , file := filepath .Split (ev .Name )
9391 for _ , path := range watch .getSameDirs (dir ) {
9492 watch .delChan <- filepath .Join (path , file )
95- watch .addLoad ( path )
96- watch .addUpdate (path )
93+ watch .addUpdate ( watchUpdate { "dir" , path } )
94+ watch .addUpdate (watchUpdate { "file" , path } )
9795 }
9896 }
9997
@@ -108,47 +106,53 @@ func (watch *watch) loop() {
108106
109107 dir , file := filepath .Split (ev .Name )
110108 for _ , path := range watch .getSameDirs (dir ) {
111- watch .addUpdate (filepath .Join (path , file ))
109+ watch .addUpdate (watchUpdate { "file" , filepath .Join (path , file )} )
112110 }
113111 }
114- case <- watch .loadTimer .C :
115- for path := range watch .loads {
116- if _ , err := os .Lstat (path ); err != nil {
117- continue
118- }
119- dir := newDir (path )
120- dir .sort ()
121- watch .dirChan <- dir
122- }
123- clear (watch .loads )
124- case <- watch .updateTimer .C :
125- for path := range watch .updates {
126- if _ , err := os .Lstat (path ); err != nil {
127- continue
128- }
129- watch .fileChan <- newFile (path )
112+ case update := <- watch .timeout :
113+ if watch .pending [update ] {
114+ watch .processUpdate (update )
115+ time .AfterFunc (100 * time .Millisecond , func () { watch .timeout <- update })
116+ watch .pending [update ] = false
117+ } else {
118+ delete (watch .pending , update )
130119 }
131- clear (watch .updates )
132120 case <- watch .quit :
133121 return
134122 }
135123 }
136124}
137125
138- func (watch * watch ) addLoad (path string ) {
139- if len (watch .loads ) == 0 {
140- watch .loadTimer .Stop ()
141- watch .loadTimer .Reset (10 * time .Millisecond )
126+ type watchUpdate struct {
127+ kind string
128+ path string
129+ }
130+
131+ func (watch * watch ) addUpdate (update watchUpdate ) {
132+ // process an update immediately if is the first time, otherwise store it
133+ // and process only after a timeout to reduce the number of actual loads
134+ if _ , ok := watch .pending [update ]; ! ok {
135+ watch .processUpdate (update )
136+ time .AfterFunc (100 * time .Millisecond , func () { watch .timeout <- update })
137+ watch .pending [update ] = false
138+ } else {
139+ watch .pending [update ] = true
142140 }
143- watch .loads [path ] = true
144141}
145142
146- func (watch * watch ) addUpdate (path string ) {
147- if len (watch .updates ) == 0 {
148- watch .updateTimer .Stop ()
149- watch .updateTimer .Reset (10 * time .Millisecond )
143+ func (watch * watch ) processUpdate (update watchUpdate ) {
144+ switch update .kind {
145+ case "dir" :
146+ if _ , err := os .Lstat (update .path ); err == nil {
147+ dir := newDir (update .path )
148+ dir .sort ()
149+ watch .dirChan <- dir
150+ }
151+ case "file" :
152+ if _ , err := os .Lstat (update .path ); err == nil {
153+ watch .fileChan <- newFile (update .path )
154+ }
150155 }
151- watch .updates [path ] = true
152156}
153157
154158// Hacky workaround since fsnotify reports changes for only one path if a
0 commit comments