2222 // from previously calling Start and not yet calling Close.
2323 ErrWatcherRunning = errors .New ("error: watcher is already running" )
2424
25+ // ErrWatcherNotRunning occurs when trying to perform a ScanNow
26+ // when the watcher is not running. It will also occur if Close is called
27+ // whilst a ScanNow() is running / pending.
28+ ErrWatcherNotRunning = errors .New ("error: watcher is not running" )
29+
2530 // ErrWatchedFileDeleted is an error that occurs when a file or folder that was
2631 // being watched has been deleted.
2732 ErrWatchedFileDeleted = errors .New ("error: watched file or folder deleted" )
@@ -129,6 +134,7 @@ type Watcher struct {
129134 ops map [Op ]struct {} // Op filtering.
130135 ignoreHidden bool // ignore hidden files or not.
131136 maxEvents int // max sent events per cycle
137+ scanNow chan chan struct {} // allows requests for immediate synchronous scans
132138}
133139
134140// New creates a new Watcher.
@@ -147,6 +153,7 @@ func New() *Watcher {
147153 files : make (map [string ]os.FileInfo ),
148154 ignored : make (map [string ]struct {}),
149155 names : make (map [string ]bool ),
156+ scanNow : make (chan chan struct {}),
150157 }
151158}
152159
@@ -550,6 +557,8 @@ func (w *Watcher) Start(d time.Duration) error {
550557 // Unblock w.Wait().
551558 w .wg .Done ()
552559
560+ var scanNowRequest chan struct {}
561+
553562 for {
554563 // done lets the inner polling cycle loop know when the
555564 // current cycle's method has finished executing.
@@ -589,7 +598,7 @@ func (w *Watcher) Start(d time.Duration) error {
589598 }
590599 }
591600 numEvents ++
592- if w .maxEvents > 0 && numEvents > w .maxEvents {
601+ if scanNowRequest == nil && w .maxEvents > 0 && numEvents > w .maxEvents {
593602 close (cancel )
594603 break inner
595604 }
@@ -604,8 +613,52 @@ func (w *Watcher) Start(d time.Duration) error {
604613 w .files = fileList
605614 w .mu .Unlock ()
606615
616+ if scanNowRequest != nil {
617+ close (scanNowRequest )
618+ scanNowRequest = nil
619+ }
620+
607621 // Sleep and then continue to the next loop iteration.
608- time .Sleep (d )
622+ // If a request to do a full scan is received, handle it and then signal to the requester it is complete.
623+ select {
624+ case <- w .close : // break out of wait early if we get a Close
625+ case scanNowRequest = <- w .scanNow : // sync scan request received
626+ case <- time .After (d ): // periodic re-roll time elapsed
627+ }
628+ }
629+ }
630+
631+ // ScanNow can be called on a already running watcher to perform an immediate synchronous scan of all watched files
632+ // and generate the events for any changes. When ScanNow() returns to the caller, all events for any changed files
633+ // have been published. ScanNow() can be used when you know FS changes have occurred and you want to ensure all events
634+ // for the changes have been gathered before continuing, for example, to better process batched updates to groups of
635+ // files.
636+ // You can also specify a very long poll duration and then use ScanNow() to break from the poll wait and perform a scan
637+ // before going back to sleep.
638+ func (w * Watcher ) ScanNow () error {
639+ w .mu .Lock ()
640+ if ! w .running {
641+ w .mu .Unlock ()
642+ return ErrWatcherNotRunning
643+ }
644+ w .mu .Unlock ()
645+
646+ scanComplete := make (chan struct {})
647+ select {
648+ case w .scanNow <- scanComplete :
649+ case <- w .close :
650+ // if the watcher is no longer running, or is closed whilst we're waiting for our scan to be accepted, return
651+ // an error
652+ return ErrWatcherNotRunning
653+ }
654+
655+ select {
656+ case <- w .close :
657+ // if the watcher is closed whilst we're waiting for our scan to complete, return an error
658+ return ErrWatcherNotRunning
659+ case <- scanComplete :
660+ // scan completed ok
661+ return nil
609662 }
610663}
611664
@@ -700,6 +753,7 @@ func (w *Watcher) Wait() {
700753}
701754
702755// Close stops a Watcher and unlocks its mutex, then sends a close signal.
756+ // Note, it is not safe to Start() a Watcher again after closing it. You must create a new Watcher.
703757func (w * Watcher ) Close () {
704758 w .mu .Lock ()
705759 if ! w .running {
@@ -711,5 +765,6 @@ func (w *Watcher) Close() {
711765 w .names = make (map [string ]bool )
712766 w .mu .Unlock ()
713767 // Send a close signal to the Start method.
714- w .close <- struct {}{}
768+ // Use a channel close() rather than a channel write, so that ScanNow() can react to the closure also.
769+ close (w .close )
715770}
0 commit comments