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 }