mirror of
https://github.com/luxfi/metric.git
synced 2026-07-27 04:12:44 +00:00
n.value++ on the noop counter was unguarded; concurrent workers in
luxfi/threshold/pkg/protocol/handler.go triggered -race failures
across protocols/{lss,frost} and pkg/thresholdd/TestCGGMP21RoundTrip.
Switch to atomic CAS over float64 bits stored in a uint64 — mirrors
metricCounter / metricGauge in metrics_impl.go so the noop and real
impls share the same internal representation (one and only one way).
Same pattern fixed in noopGauge (Set/Inc/Dec/Add/Sub/Get). noopHistogram,
noopSummary, and the *Vec wrappers are stateless / per-call fresh values
so they are race-free by construction and untouched.
Added TestNoop{Counter,Gauge}Concurrent regression guards (build-tag-free
so go test -race ./... continues to verify lock-free correctness).
Flagged by 2026-05-31 cross-repo audit.
59 lines
1.3 KiB
Go
59 lines
1.3 KiB
Go
// Copyright (C) 2020-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package metric
|
|
|
|
import (
|
|
"sync"
|
|
"testing"
|
|
)
|
|
|
|
// TestNoopCounterConcurrent guards against the 2026-05-31 regression where
|
|
// noopCounter.value was a plain float64 mutated by `n.value++`, causing
|
|
// -race failures in threshold protocols.
|
|
func TestNoopCounterConcurrent(t *testing.T) {
|
|
c := &noopCounter{}
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 64; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 1000; j++ {
|
|
c.Inc()
|
|
c.Add(1)
|
|
_ = c.Get()
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
if got, want := c.Get(), float64(64*1000*2); got != want {
|
|
t.Fatalf("noopCounter lost increments under contention: got %v want %v", got, want)
|
|
}
|
|
}
|
|
|
|
// TestNoopGaugeConcurrent guards the same regression for noopGauge.
|
|
func TestNoopGaugeConcurrent(t *testing.T) {
|
|
g := &noopGauge{}
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < 64; i++ {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
for j := 0; j < 1000; j++ {
|
|
g.Inc()
|
|
g.Add(1)
|
|
g.Dec()
|
|
g.Sub(1)
|
|
g.Set(0)
|
|
_ = g.Get()
|
|
}
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
// Net delta per iteration is 0 (Inc+Add-Dec-Sub then Set(0)), so final
|
|
// must be exactly 0 if no updates were lost or torn.
|
|
if got := g.Get(); got != 0 {
|
|
t.Fatalf("noopGauge lost updates under contention: got %v want 0", got)
|
|
}
|
|
}
|