github.com/MetalBlockchain/subnet-evm@v0.4.9/accounts/keystore/watch.go (about) 1 // (c) 2019-2020, Ava Labs, Inc. 2 // 3 // This file is a derived work, based on the go-ethereum library whose original 4 // notices appear below. 5 // 6 // It is distributed under a license compatible with the licensing terms of the 7 // original code from which it is derived. 8 // 9 // Much love to the original authors for their work. 10 // ********** 11 // Copyright 2016 The go-ethereum Authors 12 // This file is part of the go-ethereum library. 13 // 14 // The go-ethereum library is free software: you can redistribute it and/or modify 15 // it under the terms of the GNU Lesser General Public License as published by 16 // the Free Software Foundation, either version 3 of the License, or 17 // (at your option) any later version. 18 // 19 // The go-ethereum library is distributed in the hope that it will be useful, 20 // but WITHOUT ANY WARRANTY; without even the implied warranty of 21 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 22 // GNU Lesser General Public License for more details. 23 // 24 // You should have received a copy of the GNU Lesser General Public License 25 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 26 27 //go:build (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris 28 // +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris 29 30 package keystore 31 32 import ( 33 "time" 34 35 "github.com/ethereum/go-ethereum/log" 36 "github.com/rjeczalik/notify" 37 ) 38 39 type watcher struct { 40 ac *accountCache 41 starting bool 42 running bool 43 ev chan notify.EventInfo 44 quit chan struct{} 45 } 46 47 func newWatcher(ac *accountCache) *watcher { 48 return &watcher{ 49 ac: ac, 50 ev: make(chan notify.EventInfo, 10), 51 quit: make(chan struct{}), 52 } 53 } 54 55 // starts the watcher loop in the background. 56 // Start a watcher in the background if that's not already in progress. 57 // The caller must hold w.ac.mu. 58 func (w *watcher) start() { 59 if w.starting || w.running { 60 return 61 } 62 w.starting = true 63 go w.loop() 64 } 65 66 func (w *watcher) close() { 67 close(w.quit) 68 } 69 70 func (w *watcher) loop() { 71 defer func() { 72 w.ac.mu.Lock() 73 w.running = false 74 w.starting = false 75 w.ac.mu.Unlock() 76 }() 77 logger := log.New("path", w.ac.keydir) 78 79 if err := notify.Watch(w.ac.keydir, w.ev, notify.All); err != nil { 80 logger.Trace("Failed to watch keystore folder", "err", err) 81 return 82 } 83 defer notify.Stop(w.ev) 84 logger.Trace("Started watching keystore folder") 85 defer logger.Trace("Stopped watching keystore folder") 86 87 w.ac.mu.Lock() 88 w.running = true 89 w.ac.mu.Unlock() 90 91 // Wait for file system events and reload. 92 // When an event occurs, the reload call is delayed a bit so that 93 // multiple events arriving quickly only cause a single reload. 94 var ( 95 debounceDuration = 500 * time.Millisecond 96 rescanTriggered = false 97 debounce = time.NewTimer(0) 98 ) 99 // Ignore initial trigger 100 if !debounce.Stop() { 101 <-debounce.C 102 } 103 defer debounce.Stop() 104 for { 105 select { 106 case <-w.quit: 107 return 108 case <-w.ev: 109 // Trigger the scan (with delay), if not already triggered 110 if !rescanTriggered { 111 debounce.Reset(debounceDuration) 112 rescanTriggered = true 113 } 114 case <-debounce.C: 115 w.ac.scanAccounts() 116 rescanTriggered = false 117 } 118 } 119 }