Initialize atomics in metrics, update style guide on atomics

This commit is contained in:
2025-08-29 10:52:26 -04:00
parent 1133d1e365
commit b6d4ae2862
3 changed files with 31 additions and 3 deletions

View File

@@ -176,6 +176,7 @@ struct Metric {
family->p->perThreadState[std::this_thread::get_id()].instances[key]; family->p->perThreadState[std::this_thread::get_id()].instances[key];
if (!ptr) { if (!ptr) {
ptr = std::make_unique<Counter::State>(); ptr = std::make_unique<Counter::State>();
ptr->value.store(0, std::memory_order_relaxed);
} }
Counter result; Counter result;
result.p = ptr.get(); result.p = ptr.get();
@@ -190,6 +191,7 @@ struct Metric {
auto &ptr = family->p->instances[key]; auto &ptr = family->p->instances[key];
if (!ptr) { if (!ptr) {
ptr = std::make_unique<Gauge::State>(); ptr = std::make_unique<Gauge::State>();
ptr->value = 0.0;
} }
Gauge result; Gauge result;
result.p = ptr.get(); result.p = ptr.get();
@@ -210,8 +212,12 @@ struct Metric {
ptr->thresholds = family->p->buckets; // Already sorted and deduplicated ptr->thresholds = family->p->buckets; // Already sorted and deduplicated
// DESIGN: std::atomic is not copy-constructible // DESIGN: std::atomic is not copy-constructible
// Initialize vector with correct size, all atomics default to 0 // Initialize vector with correct size, all atomics explicitly initialized
// to 0
ptr->counts = std::vector<AtomicWord>(ptr->thresholds.size()); ptr->counts = std::vector<AtomicWord>(ptr->thresholds.size());
for (auto &count : ptr->counts) {
count.store(0, std::memory_order_relaxed);
}
ptr->sum.store(0, std::memory_order_relaxed); ptr->sum.store(0, std::memory_order_relaxed);
ptr->observations.store(0, std::memory_order_relaxed); ptr->observations.store(0, std::memory_order_relaxed);
} }
@@ -232,8 +238,8 @@ void Counter::inc(double x) {
std::memory_order_relaxed); std::memory_order_relaxed);
} }
void Gauge::inc(double x) { void Gauge::inc(double x) {
// IMPLEMENTATION DETAIL: Gauges currently support multiple writers via mutex, // IMPLEMENTATION DETAIL: Mutex protection used internally for thread safety,
// though the public API documents single-writer semantics for consistency // but API contract remains single-writer per instance
std::unique_lock<std::mutex> _{p->mutex}; std::unique_lock<std::mutex> _{p->mutex};
p->value += x; p->value += x;
} }

View File

@@ -58,6 +58,7 @@ private:
// THREAD SAFETY: Each gauge instance has exactly ONE writer thread (the one // THREAD SAFETY: Each gauge instance has exactly ONE writer thread (the one
// that created it). It is an error to call inc()/dec()/set() from any thread // that created it). It is an error to call inc()/dec()/set() from any thread
// other than the creating thread. // other than the creating thread.
// IMPLEMENTATION NOTE: Mutex protection is an internal implementation detail.
struct Gauge { struct Gauge {
void inc(double = 1.0); // Increase gauge value - SINGLE WRITER ONLY void inc(double = 1.0); // Increase gauge value - SINGLE WRITER ONLY
void dec(double = 1.0); // Decrease gauge value - SINGLE WRITER ONLY void dec(double = 1.0); // Decrease gauge value - SINGLE WRITER ONLY

View File

@@ -301,6 +301,27 @@ for (auto &precondition : preconditions_) {
} }
``` ```
### Atomic Operations
- **Never use assignment operators** with `std::atomic` - always use explicit `store()` and `load()`
- **Always specify memory ordering** explicitly for atomic operations
- **Use the least restrictive correct memory ordering** - choose the weakest ordering that maintains correctness
```cpp
// Preferred - explicit store/load with precise memory ordering
std::atomic<uint64_t> counter;
counter.store(42, std::memory_order_relaxed); // Single-writer metric updates
auto value = counter.load(std::memory_order_relaxed); // Reading metrics for display
counter.store(1, std::memory_order_release); // Publishing initialization
auto ready = counter.load(std::memory_order_acquire); // Synchronizing with publisher
counter.store(42, std::memory_order_seq_cst); // When sequential consistency needed
// Avoid - assignment operators (implicit memory ordering)
std::atomic<uint64_t> counter;
counter = 42; // Implicit - memory ordering not explicit
auto value = counter; // Implicit - memory ordering not explicit
```
--- ---
## Memory Management ## Memory Management