From 1179c247afbc815a664e26fd58a52f75bec15ccd Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 23 May 2026 22:08:13 +0300 Subject: doc: document MetricRegistry pointer safety guarantees Add explicit documentation to Find, FindCaseInsensitive, FindWithAliases, List, and ListByCategory clarifying that returned *Metric pointers are safe to hold across concurrent Unregister calls. Metric objects are immutable after registration: Register panics on duplicate names (no in-place mutation), Unregister only deletes entries from the map (never modifies the Metric struct), and no mutator methods exist on the Metric type. The sync.RWMutex protects the map structures, while the immutability invariant protects the returned pointers. --- internal/rpn/metric_registry.go | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/internal/rpn/metric_registry.go b/internal/rpn/metric_registry.go index 7a8b9aa..2c89b11 100644 --- a/internal/rpn/metric_registry.go +++ b/internal/rpn/metric_registry.go @@ -53,6 +53,11 @@ func (r *MetricRegistry) Register(m *Metric) { } // Find looks up a metric by name (case-sensitive). +// The returned *Metric pointer is safe to hold across concurrent Unregister +// calls: Metric objects are never mutated after registration (Register panics +// on duplicates, and Unregister only deletes entries from the map without +// modifying the objects), so the pointer always refers to a valid, immutable +// value even after the metric is unregistered from the registry. func (r *MetricRegistry) Find(name string) (*Metric, bool) { r.mu.RLock() defer r.mu.RUnlock() @@ -62,6 +67,8 @@ func (r *MetricRegistry) Find(name string) (*Metric, bool) { // FindCaseInsensitive looks up a metric by name ignoring case. // Returns the canonical name match. +// The returned *Metric pointer is safe to hold across concurrent operations +// (see MetricRegistry.Find for details). func (r *MetricRegistry) FindCaseInsensitive(name string) (*Metric, bool) { r.mu.RLock() defer r.mu.RUnlock() @@ -105,6 +112,8 @@ func (r *MetricRegistry) MarkExactMatch(names ...string) { // FindWithAliases looks up a metric by name, resolving aliases. // Checks exact match first, then aliases, then case-insensitive (unless exact-match). +// The returned *Metric pointer is safe to hold across concurrent operations +// (see MetricRegistry.Find for details). func (r *MetricRegistry) FindWithAliases(name string) (*Metric, bool) { r.mu.RLock() defer r.mu.RUnlock() @@ -137,7 +146,9 @@ func (r *MetricRegistry) FindWithAliases(name string) (*Metric, bool) { return nil, false } -// List returns all registered metrics. +// List returns all registered metrics as a slice of pointers. +// The returned pointers are safe to hold across concurrent Unregister calls +// (see MetricRegistry.Find for details). func (r *MetricRegistry) List() []*Metric { r.mu.RLock() defer r.mu.RUnlock() @@ -149,6 +160,8 @@ func (r *MetricRegistry) List() []*Metric { } // ListByCategory returns all metrics in the given category. +// The returned pointers are safe to hold across concurrent Unregister calls +// (see MetricRegistry.Find for details). func (r *MetricRegistry) ListByCategory(cat Category) []*Metric { r.mu.RLock() defer r.mu.RUnlock() -- cgit v1.2.3