github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/accounts/watch.go (about)

     1  // Copyright 2016 The go-ethereum Authors
     2  // This file is part of the go-ethereum library.
     3  //
     4  // The go-ethereum library is free software: you can redistribute it and/or modify
     5  // it under the terms of the GNU Lesser General Public License as published by
     6  // the Free Software Foundation, either version 3 of the License, or
     7  // (at your option) any later version.
     8  //
     9  // The go-ethereum library is distributed in the hope that it will be useful,
    10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    12  // GNU Lesser General Public License for more details.
    13  //
    14  // You should have received a copy of the GNU Lesser General Public License
    15  // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  // +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows
    18  
    19  package accounts
    20  
    21  import (
    22  	"time"
    23  
    24  	"github.com/ethereumproject/go-ethereum/logger"
    25  	"github.com/ethereumproject/go-ethereum/logger/glog"
    26  	"github.com/rjeczalik/notify"
    27  )
    28  
    29  type watcher struct {
    30  	ac       caching
    31  	starting bool
    32  	running  bool
    33  	ev       chan notify.EventInfo
    34  	//evs      []notify.EventInfo
    35  	quit chan struct{}
    36  }
    37  
    38  func newWatcher(ac caching) *watcher {
    39  	return &watcher{
    40  		ac:   ac,
    41  		ev:   make(chan notify.EventInfo, 10),
    42  		quit: make(chan struct{}),
    43  	}
    44  }
    45  
    46  // starts the watcher loop in the background.
    47  // Start a watcher in the background if that's not already in progress.
    48  // The caller must hold w.ac.mu.
    49  func (w *watcher) start() {
    50  	if w.starting || w.running {
    51  		return
    52  	}
    53  	w.starting = true
    54  	go w.loop()
    55  }
    56  
    57  func (w *watcher) close() {
    58  	close(w.quit)
    59  }
    60  
    61  func (w *watcher) loop() {
    62  	defer func() {
    63  		w.ac.muLock()
    64  		w.running = false
    65  		w.starting = false
    66  		w.ac.muUnlock()
    67  	}()
    68  
    69  	err := notify.Watch(w.ac.getKeydir(), w.ev, notify.All)
    70  	if err != nil {
    71  		glog.V(logger.Warn).Errorf("can't watch %s: %v", w.ac.getKeydir(), err)
    72  		return
    73  	}
    74  	defer notify.Stop(w.ev)
    75  	glog.V(logger.Detail).Infof("now watching %s", w.ac.getKeydir())
    76  	defer glog.V(logger.Detail).Infof("no longer watching %s", w.ac.getKeydir())
    77  
    78  	w.ac.muLock()
    79  	w.running = true
    80  	w.ac.muUnlock()
    81  
    82  	// Wait for file system events and reload.
    83  	// When an event occurs, the reload call is delayed a bit so that
    84  	// multiple events arriving quickly only cause a single reload.
    85  	var (
    86  		debounce          = time.NewTimer(0)
    87  		debounceDuration  = 500 * time.Millisecond
    88  		inCycle, hadEvent bool
    89  	)
    90  	defer debounce.Stop()
    91  	for {
    92  		select {
    93  		case <-w.quit:
    94  			return
    95  		case <-w.ev:
    96  			if !inCycle {
    97  				debounce.Reset(debounceDuration)
    98  				inCycle = true
    99  			} else {
   100  				hadEvent = true
   101  			}
   102  			//w.evs = append(w.evs, <-w.ev)
   103  		case <-debounce.C:
   104  			w.ac.muLock()
   105  			//w.evs = w.ac.reload(w.evs)
   106  			w.ac.reload()
   107  			w.ac.muUnlock()
   108  			if hadEvent {
   109  				debounce.Reset(debounceDuration)
   110  				inCycle, hadEvent = true, false
   111  
   112  			} else {
   113  				inCycle, hadEvent = false, false
   114  			}
   115  		}
   116  	}
   117  }