github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/accounts/keystore/watch.go (about)

     1  // +build darwin,!ios freebsd linux,!arm64 netbsd solaris
     2  
     3  package keystore
     4  
     5  import (
     6  	"time"
     7  
     8  	"github.com/quickchainproject/quickchain/log"
     9  	"github.com/rjeczalik/notify"
    10  )
    11  
    12  type watcher struct {
    13  	ac       *accountCache
    14  	starting bool
    15  	running  bool
    16  	ev       chan notify.EventInfo
    17  	quit     chan struct{}
    18  }
    19  
    20  func newWatcher(ac *accountCache) *watcher {
    21  	return &watcher{
    22  		ac:   ac,
    23  		ev:   make(chan notify.EventInfo, 10),
    24  		quit: make(chan struct{}),
    25  	}
    26  }
    27  
    28  // starts the watcher loop in the background.
    29  // Start a watcher in the background if that's not already in progress.
    30  // The caller must hold w.ac.mu.
    31  func (w *watcher) start() {
    32  	if w.starting || w.running {
    33  		return
    34  	}
    35  	w.starting = true
    36  	go w.loop()
    37  }
    38  
    39  func (w *watcher) close() {
    40  	close(w.quit)
    41  }
    42  
    43  func (w *watcher) loop() {
    44  	defer func() {
    45  		w.ac.mu.Lock()
    46  		w.running = false
    47  		w.starting = false
    48  		w.ac.mu.Unlock()
    49  	}()
    50  	logger := log.New("path", w.ac.keydir)
    51  
    52  	if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil {
    53  		logger.Trace("Failed to watch keystore folder", "err", err)
    54  		return
    55  	}
    56  	defer notify.Stop(w.ev)
    57  	logger.Trace("Started watching keystore folder")
    58  	defer logger.Trace("Stopped watching keystore folder")
    59  
    60  	w.ac.mu.Lock()
    61  	w.running = true
    62  	w.ac.mu.Unlock()
    63  
    64  	// Wait for file system events and reload.
    65  	// When an event occurs, the reload call is delayed a bit so that
    66  	// multiple events arriving quickly only cause a single reload.
    67  	var (
    68  		debounceDuration = 500 * time.Millisecond
    69  		rescanTriggered  = false
    70  		debounce         = time.NewTimer(0)
    71  	)
    72  	// Ignore initial trigger
    73  	if !debounce.Stop() {
    74  		<-debounce.C
    75  	}
    76  	defer debounce.Stop()
    77  	for {
    78  		select {
    79  		case <-w.quit:
    80  			return
    81  		case <-w.ev:
    82  			// Trigger the scan (with delay), if not already triggered
    83  			if !rescanTriggered {
    84  				debounce.Reset(debounceDuration)
    85  				rescanTriggered = true
    86  			}
    87  		case <-debounce.C:
    88  			w.ac.scanAccounts()
    89  			rescanTriggered = false
    90  		}
    91  	}
    92  }