diff --git a/prometheus/counter_test.go b/prometheus/counter_test.go index 3d8bc37..67391a2 100644 --- a/prometheus/counter_test.go +++ b/prometheus/counter_test.go @@ -14,6 +14,7 @@ package prometheus import ( + "math" "testing" dto "github.com/prometheus/client_model/go" @@ -26,11 +27,11 @@ func TestCounterAdd(t *testing.T) { ConstLabels: Labels{"a": "1", "b": "2"}, }).(*counter) counter.Inc() - if expected, got := 1., counter.val; expected != got { + if expected, got := 1., math.Float64frombits(counter.valBits); expected != got { t.Errorf("Expected %f, got %f.", expected, got) } counter.Add(42) - if expected, got := 43., counter.val; expected != got { + if expected, got := 43., math.Float64frombits(counter.valBits); expected != got { t.Errorf("Expected %f, got %f.", expected, got) } diff --git a/prometheus/gauge_test.go b/prometheus/gauge_test.go index 6ff1d33..48cab46 100644 --- a/prometheus/gauge_test.go +++ b/prometheus/gauge_test.go @@ -82,7 +82,7 @@ func TestGaugeConcurrency(t *testing.T) { } start.Done() - if expected, got := <-result, gge.(*value).val; math.Abs(expected-got) > 0.000001 { + if expected, got := <-result, math.Float64frombits(gge.(*value).valBits); math.Abs(expected-got) > 0.000001 { t.Fatalf("expected approx. %f, got %f", expected, got) return false } @@ -146,7 +146,7 @@ func TestGaugeVecConcurrency(t *testing.T) { start.Done() for i := range sStreams { - if expected, got := <-results[i], gge.WithLabelValues(string('A'+i)).(*value).val; math.Abs(expected-got) > 0.000001 { + if expected, got := <-results[i], math.Float64frombits(gge.WithLabelValues(string('A'+i)).(*value).valBits); math.Abs(expected-got) > 0.000001 { t.Fatalf("expected approx. %f, got %f", expected, got) return false } diff --git a/prometheus/value.go b/prometheus/value.go index be74b7f..2db1d11 100644 --- a/prometheus/value.go +++ b/prometheus/value.go @@ -16,8 +16,9 @@ package prometheus import ( "errors" "fmt" + "math" "sort" - "sync" + "sync/atomic" dto "github.com/prometheus/client_model/go" @@ -44,10 +45,9 @@ var errInconsistentCardinality = errors.New("inconsistent label cardinality") type value struct { SelfCollector - mtx sync.RWMutex desc *Desc valType ValueType - val float64 + valBits uint64 // These are the bits of the represented float64 value. labelPairs []*dto.LabelPair } @@ -61,7 +61,7 @@ func newValue(desc *Desc, valueType ValueType, val float64, labelValues ...strin result := &value{ desc: desc, valType: valueType, - val: val, + valBits: math.Float64bits(val), labelPairs: makeLabelPairs(desc, labelValues), } result.Init(result) @@ -73,10 +73,7 @@ func (v *value) Desc() *Desc { } func (v *value) Set(val float64) { - v.mtx.Lock() - defer v.mtx.Unlock() - - v.val = val + atomic.StoreUint64(&v.valBits, math.Float64bits(val)) } func (v *value) Inc() { @@ -88,10 +85,13 @@ func (v *value) Dec() { } func (v *value) Add(val float64) { - v.mtx.Lock() - defer v.mtx.Unlock() - - v.val += val + for { + oldBits := atomic.LoadUint64(&v.valBits) + newBits := math.Float64bits(math.Float64frombits(oldBits) + val) + if atomic.CompareAndSwapUint64(&v.valBits, oldBits, newBits) { + return + } + } } func (v *value) Sub(val float64) { @@ -99,10 +99,7 @@ func (v *value) Sub(val float64) { } func (v *value) Write(out *dto.Metric) error { - v.mtx.RLock() - val := v.val - v.mtx.RUnlock() - + val := math.Float64frombits(atomic.LoadUint64(&v.valBits)) return populateMetric(v.valType, val, v.labelPairs, out) }