From 1133d1e365dfe79aca347486a21a3ad18c11d04d Mon Sep 17 00:00:00 2001 From: Andrew Noyes Date: Fri, 29 Aug 2025 10:45:19 -0400 Subject: [PATCH] Use std::bit_cast, document that gauge mutex is an implementation detail --- src/metric.cpp | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/metric.cpp b/src/metric.cpp index d77413a..fec2708 100644 --- a/src/metric.cpp +++ b/src/metric.cpp @@ -8,7 +8,7 @@ // // PRECISION STRATEGY: // - Use atomic for lock-free storage -// - Store doubles by reinterpreting bits as uint64_t (preserves full IEEE 754 +// - Store doubles using std::bit_cast to uint64_t (preserves full IEEE 754 // precision) // - Single writer assumption allows simple load/store without CAS loops // @@ -19,9 +19,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -74,15 +74,7 @@ using AtomicWord = std::atomic; // DESIGN: Store doubles in atomic for lock-free operations // - Preserves full IEEE 754 double precision (no truncation) // - Allows atomic load/store without locks -// - Safe bit-wise conversion between double and uint64_t -template U reinterpret(V v) { - static_assert(sizeof(U) == sizeof(V)); - static_assert(std::is_arithmetic_v); - static_assert(std::is_arithmetic_v); - U u; - memcpy(&u, &v, sizeof(u)); - return u; -} +// - Use std::bit_cast for safe conversion between double and uint64_t // Family::State structures own the second-level maps (labels -> instances) template <> struct Family::State { @@ -235,11 +227,13 @@ void Counter::inc(double x) { // DESIGN: Single writer per thread allows simple load-modify-store // No CAS loop needed since only one thread writes to this counter auto current_value = - reinterpret(p->value.load(std::memory_order_relaxed)); - p->value.store(reinterpret(current_value + x), + std::bit_cast(p->value.load(std::memory_order_relaxed)); + p->value.store(std::bit_cast(current_value + 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 std::unique_lock _{p->mutex}; p->value += x; } @@ -263,8 +257,8 @@ void Histogram::observe(double x) { // DESIGN: Single writer per thread allows simple load-modify-store for sum // No CAS loop needed since only one thread writes to this histogram auto current_sum = - reinterpret(p->sum.load(std::memory_order_relaxed)); - p->sum.store(reinterpret(current_sum + x), + std::bit_cast(p->sum.load(std::memory_order_relaxed)); + p->sum.store(std::bit_cast(current_sum + x), std::memory_order_relaxed); p->observations.fetch_add(1, std::memory_order_relaxed);