diff --git a/src/metric.cpp b/src/metric.cpp index fec2708..e37bf3a 100644 --- a/src/metric.cpp +++ b/src/metric.cpp @@ -176,6 +176,7 @@ struct Metric { family->p->perThreadState[std::this_thread::get_id()].instances[key]; if (!ptr) { ptr = std::make_unique(); + ptr->value.store(0, std::memory_order_relaxed); } Counter result; result.p = ptr.get(); @@ -190,6 +191,7 @@ struct Metric { auto &ptr = family->p->instances[key]; if (!ptr) { ptr = std::make_unique(); + ptr->value = 0.0; } Gauge result; result.p = ptr.get(); @@ -210,8 +212,12 @@ struct Metric { ptr->thresholds = family->p->buckets; // Already sorted and deduplicated // 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(ptr->thresholds.size()); + for (auto &count : ptr->counts) { + count.store(0, std::memory_order_relaxed); + } ptr->sum.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); } void Gauge::inc(double x) { - // IMPLEMENTATION DETAIL: Gauges currently support multiple writers via mutex, - // though the public API documents single-writer semantics for consistency + // IMPLEMENTATION DETAIL: Mutex protection used internally for thread safety, + // but API contract remains single-writer per instance std::unique_lock _{p->mutex}; p->value += x; } diff --git a/src/metric.hpp b/src/metric.hpp index b0e15f2..b2e83cd 100644 --- a/src/metric.hpp +++ b/src/metric.hpp @@ -58,6 +58,7 @@ private: // 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 // other than the creating thread. +// IMPLEMENTATION NOTE: Mutex protection is an internal implementation detail. struct Gauge { void inc(double = 1.0); // Increase gauge value - SINGLE WRITER ONLY void dec(double = 1.0); // Decrease gauge value - SINGLE WRITER ONLY diff --git a/style.md b/style.md index b589903..f2da1e0 100644 --- a/style.md +++ b/style.md @@ -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 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 counter; +counter = 42; // Implicit - memory ordering not explicit +auto value = counter; // Implicit - memory ordering not explicit +``` + --- ## Memory Management