gauge.go: add Inc, Dec and Add methods to Gauge

This commit is contained in:
Aliaksandr Valialkin 2024-02-18 12:40:32 +02:00
parent fdfd428a62
commit da211e52b9
No known key found for this signature in database
GPG Key ID: 52C003EE2BCDB9EB
2 changed files with 88 additions and 1 deletions

View File

@ -17,7 +17,7 @@ import (
// - foo{bar="baz",aaa="b"}
//
// f must be safe for concurrent calls.
// if f is nil, then it is expected that the gauge value is changed via Gauge.Set() call.
// if f is nil, then it is expected that the gauge value is changed via Set(), Inc(), Dec() and Add() calls.
//
// The returned gauge is safe to use from concurrent goroutines.
//
@ -55,6 +55,38 @@ func (g *Gauge) Set(v float64) {
atomic.StoreUint64(&g.valueBits, n)
}
// Inc increments g by 1.
//
// The g must be created with nil callback in order to be able to call this function.
func (g *Gauge) Inc() {
g.Add(1)
}
// Dec decrements g by 1.
//
// The g must be created with nil callback in order to be able to call this function.
func (g *Gauge) Dec() {
g.Add(-1)
}
// Add adds fAdd to g. fAdd may be positive and negative.
//
// The g must be created with nil callback in order to be able to call this function.
func (g *Gauge) Add(fAdd float64) {
if g.f != nil {
panic(fmt.Errorf("cannot call Set on gauge created with non-nil callback"))
}
for {
n := atomic.LoadUint64(&g.valueBits)
f := math.Float64frombits(n)
fNew := f + fAdd
nNew := math.Float64bits(fNew)
if atomic.CompareAndSwapUint64(&g.valueBits, n, nNew) {
break
}
}
}
func (g *Gauge) marshalTo(prefix string, w io.Writer) {
v := g.Get()
if float64(int64(v)) == v {

View File

@ -15,6 +15,18 @@ func TestGaugeError(t *testing.T) {
g := GetOrCreateGauge("GetOrCreateGauge_nil_callback", func() float64 { return 123 })
g.Set(42)
})
expectPanic(t, "GetOrCreateGauge_Add_non-nil-callback", func() {
g := GetOrCreateGauge("GetOrCreateGauge_nil_callback", func() float64 { return 123 })
g.Add(42)
})
expectPanic(t, "GetOrCreateGauge_Inc_non-nil-callback", func() {
g := GetOrCreateGauge("GetOrCreateGauge_nil_callback", func() float64 { return 123 })
g.Inc()
})
expectPanic(t, "GetOrCreateGauge_Dec_non-nil-callback", func() {
g := GetOrCreateGauge("GetOrCreateGauge_nil_callback", func() float64 { return 123 })
g.Dec()
})
}
func TestGaugeSet(t *testing.T) {
@ -29,6 +41,49 @@ func TestGaugeSet(t *testing.T) {
}
}
func TestGaugeIncDec(t *testing.T) {
s := NewSet()
g := s.NewGauge("foo", nil)
if n := g.Get(); n != 0 {
t.Fatalf("unexpected gauge value: %g; expecting 0", n)
}
for i := 1; i <= 100; i++ {
g.Inc()
if n := g.Get(); n != float64(i) {
t.Fatalf("unexpected gauge value %g; expecting %d", n, i)
}
}
for i := 99; i >= 0; i-- {
g.Dec()
if n := g.Get(); n != float64(i) {
t.Fatalf("unexpected gauge value %g; expecting %d", n, i)
}
}
}
func TestGaugeIncDecConcurrenc(t *testing.T) {
s := NewSet()
g := s.NewGauge("foo", nil)
workers := 5
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
for i := 0; i < 100; i++ {
g.Inc()
g.Dec()
}
wg.Done()
}()
}
wg.Wait()
if n := g.Get(); n != 0 {
t.Fatalf("unexpected gauge value %g; want 0", n)
}
}
func TestGaugeSerial(t *testing.T) {
name := "GaugeSerial"
n := 1.23