github.com/zeebo/mon@v0.0.0-20211012163247-13d39bdb54fa/state.go (about) 1 package mon 2 3 import ( 4 "sync" 5 "sync/atomic" 6 "unsafe" 7 8 "github.com/zeebo/mon/internal/lfht" 9 "github.com/zeebo/mon/inthist" 10 "github.com/zeebo/swaparoo" 11 ) 12 13 var ( 14 statesMu sync.Mutex // protects concurrent Collect calls. 15 states [2]lfht.Table // states maps names to State pointers. 16 tracker swaparoo.Tracker // keeps track of which state is valid. 17 ) 18 19 func newState() unsafe.Pointer { return unsafe.Pointer(new(State)) } 20 func newCounter() unsafe.Pointer { return unsafe.Pointer(new(int64)) } 21 22 // State keeps track of all of the timer information for some calls. 23 type State struct { 24 errors lfht.Table 25 his inthist.Histogram 26 } 27 28 // Times calls the callback with all of the histograms that have been captured. 29 func Times(cb func(string, *State) bool) { 30 token := tracker.Acquire() 31 for iter := states[token.Gen()%2].Iterator(); iter.Next(); { 32 if !cb(iter.Key(), (*State)(iter.Value())) { 33 goto done 34 } 35 } 36 done: 37 token.Release() 38 } 39 40 // Collect consumes all of the histograms that have been captures and calls 41 // the callback for each one. 42 func Collect(cb func(string, *State) bool) { 43 statesMu.Lock() 44 gen := tracker.Increment().Wait() 45 for iter := states[gen%2].Iterator(); iter.Next(); { 46 if !cb(iter.Key(), (*State)(iter.Value())) { 47 goto done 48 } 49 } 50 done: 51 states[gen%2] = lfht.Table{} 52 statesMu.Unlock() 53 } 54 55 // GetState returns the current state for some name, allocating a new one if necessary. 56 func GetState(name string) *State { 57 token := tracker.Acquire() 58 state := (*State)(states[token.Gen()%2].Upsert(name, newState)) 59 token.Release() 60 return state 61 } 62 63 // LookupState returns the current state for some name, returning nil if none exists. 64 func LookupState(name string) *State { 65 token := tracker.Acquire() 66 state := (*State)(states[token.Gen()%2].Lookup(name)) 67 token.Release() 68 return state 69 } 70 71 // done informs the State that a task has completed in the given 72 // amount of nanoseconds. 73 func (s *State) done(v int64, kind string) { 74 s.his.Observe(v) 75 if kind != "" { 76 counter := (*int64)(s.errors.Upsert(kind, newCounter)) 77 atomic.AddInt64(counter, 1) 78 } 79 } 80 81 // Histogram returns the Histogram associated with the state. 82 func (s *State) Histogram() *inthist.Histogram { return &s.his } 83 84 // Errors returns a tree of error counters. Be sure to use atomic.LoadInt64 on the results. 85 func (s *State) Errors() *lfht.Table { return &s.errors } 86 87 // Total returns the number of completed calls. 88 func (s *State) Total() int64 { return s.his.Total() } 89 90 // Quantile returns an estimation of the qth quantile in [0, 1]. 91 func (s *State) Quantile(q float64) int64 { return s.his.Quantile(q) } 92 93 // Sum returns an estimation of the sum. 94 func (s *State) Sum() float64 { return s.his.Sum() } 95 96 // Average returns an estimation of the sum and average. 97 func (s *State) Average() (float64, float64) { return s.his.Average() } 98 99 // Variance returns an estimation of the sum, average and variance. 100 func (s *State) Variance() (float64, float64, float64) { return s.his.Variance() }