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() }