blob: 3beac9a22998c6111afc4b540140746c95bf318c [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef BASE_METRICS_SAMPLE_MAP_ITERATOR_H_
#define BASE_METRICS_SAMPLE_MAP_ITERATOR_H_
#include <stdint.h>
#include <atomic>
#include <type_traits>
#include <utility>
#include "base/check.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/histogram_base.h"
#include "base/metrics/histogram_samples.h"
#include "base/types/to_address.h"
namespace base {
// An iterator for going through a SampleMap. `MapT` is the underlying map type
// that stores the counts. `support_extraction` should be true iff the caller
// wants this iterator to support extracting the values. If the counts are
// pointers, accesses to them will be atomic; see `kUseAtomicOps` below.
template <typename MapT, bool support_extraction>
class SampleMapIterator : public SampleCountIterator {
private:
using T = std::conditional_t<support_extraction, MapT, const MapT>;
public:
explicit SampleMapIterator(T& sample_counts)
: iter_(sample_counts.begin()), end_(sample_counts.end()) {
SkipEmptyBuckets();
}
~SampleMapIterator() override {
if constexpr (support_extraction) {
// Ensure that the user has consumed all the samples in order to ensure no
// samples are lost.
DCHECK(Done());
}
}
// SampleCountIterator:
bool Done() const override { return iter_ == end_; }
void Next() override {
DCHECK(!Done());
++iter_;
SkipEmptyBuckets();
}
void Get(HistogramBase::Sample32* min,
int64_t* max,
HistogramBase::Count32* count) override {
DCHECK(!Done());
*min = iter_->first;
*max = int64_t{iter_->first} + 1;
if constexpr (support_extraction) {
*count = Exchange();
} else {
*count = Load();
}
}
private:
using I = std::conditional_t<support_extraction,
typename T::iterator,
typename T::const_iterator>;
// If the counts are pointers, assume they may live in shared memory, which
// means accesses to them must be atomic, since other processes may attempt to
// concurrently modify their values. (Note that a lock wouldn't help here,
// since said other processes would not be aware of our lock.) If they are
// values, we don't bother with atomic ops; callers who want thread-safety can
// use locking.
static constexpr bool kUseAtomicOps =
IsPointerOrRawPtr<typename T::mapped_type>;
void SkipEmptyBuckets() {
while (!Done() && Load() == 0) {
++iter_;
}
}
HistogramBase::Count32 Load() const {
if constexpr (kUseAtomicOps) {
return iter_->second->load(std::memory_order_relaxed);
} else {
return iter_->second;
}
}
HistogramBase::Count32 Exchange() const
requires support_extraction
{
if constexpr (kUseAtomicOps) {
return iter_->second->exchange(0, std::memory_order_relaxed);
} else {
return std::exchange(iter_->second, 0);
}
}
I iter_;
const I end_;
};
} // namespace base
#endif // BASE_METRICS_SAMPLE_MAP_ITERATOR_H_