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