github.com/rudderlabs/rudder-go-kit@v0.30.0/stats/metric/counter.go (about)

     1  package metric
     2  
     3  import (
     4  	"errors"
     5  	"math"
     6  	"sync/atomic"
     7  )
     8  
     9  // Counter counts monotonically increasing values
    10  type Counter interface {
    11  	// Inc increments the counter by 1. Use Add to increment it by arbitrary
    12  	// non-negative values.
    13  	Inc()
    14  	// Add adds the given value to the counter. It panics if the value is <
    15  	// 0.
    16  	Add(float64)
    17  	// Value gets the current value of the counter.
    18  	Value() float64
    19  }
    20  
    21  // NewCounter creates a new counter
    22  func NewCounter() Counter {
    23  	result := &counter{}
    24  	return result
    25  }
    26  
    27  type counter struct {
    28  	// valBits contains the bits of the represented float64 value, while
    29  	// valInt stores values that are exact integers. Both have to go first
    30  	// in the struct to guarantee alignment for atomic operations.
    31  	// http://golang.org/pkg/sync/atomic/#pkg-note-BUG
    32  	valBits uint64
    33  	valInt  uint64
    34  }
    35  
    36  func (c *counter) Add(v float64) {
    37  	if v < 0 {
    38  		panic(errors.New("counter cannot decrease in value"))
    39  	}
    40  
    41  	ival := uint64(v)
    42  	if float64(ival) == v {
    43  		atomic.AddUint64(&c.valInt, ival)
    44  		return
    45  	}
    46  
    47  	for {
    48  		oldBits := atomic.LoadUint64(&c.valBits)
    49  		newBits := math.Float64bits(math.Float64frombits(oldBits) + v)
    50  		if atomic.CompareAndSwapUint64(&c.valBits, oldBits, newBits) {
    51  			return
    52  		}
    53  	}
    54  }
    55  
    56  func (c *counter) Inc() {
    57  	atomic.AddUint64(&c.valInt, 1)
    58  }
    59  
    60  func (c *counter) Value() float64 {
    61  	fval := math.Float64frombits(atomic.LoadUint64(&c.valBits))
    62  	ival := atomic.LoadUint64(&c.valInt)
    63  	return fval + float64(ival)
    64  }