userver: userver/concurrent/striped_counter.hpp Source File
Loading...
Searching...
No Matches
striped_counter.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/concurrent/striped_counter.hpp
4/// @brief @copybrief concurrent::StripedCounter
5
6#include <cstdint>
7
8#include <userver/utils/fast_pimpl.hpp>
9
10USERVER_NAMESPACE_BEGIN
11
12namespace concurrent {
13
14/// @ingroup userver_concurrency
15///
16/// @brief A contention-free sharded atomic counter, with memory consumption
17/// and read performance traded for write performance. Intended to be used for
18/// write-heavy counters, mostly in metrics.
19///
20/// @note Depending on the underlying platform is implemented either via a
21/// single atomic variable, or an 'nproc'-sized array of interference-shielded
22/// rseq-based (https://www.phoronix.com/news/Restartable-Sequences-Speed)
23/// per-CPU counters.
24/// In the second case, read is approx. `nproc` times slower than write.
25class StripedCounter final {
26public:
27 /// @brief Constructs a zero-initialized counter.
28 /// Might allocate up to kDestructiveInterferenceSize (64 bytes for x86_64) *
29 /// number of available CPUs bytes.
31 ~StripedCounter();
32
33 StripedCounter(const StripedCounter&) = delete;
34 StripedCounter& operator=(const StripedCounter&) = delete;
35
36 StripedCounter(StripedCounter&&) = delete;
37 StripedCounter& operator=(StripedCounter&&) = delete;
38
39 /// @brief The addition is done with a relaxed memory order.
40 void Add(std::uintptr_t value) noexcept;
41
42 /// @brief The subtraction is done with a relaxed memory order.
43 void Subtract(std::uintptr_t value) noexcept {
44 // Perfectly defined unsigned wrapping
45 Add(-value);
46 }
47
48 /// @brief Read the the total counter value. The counter uses the full range
49 /// of `std::uintptr_t`, using wrap-around when necessary.
50 /// @note The read is done with `std::memory_order_acquire`.
51 ///
52 /// Due to the underlying implementation being an array of counters, this
53 /// function may return logically impossible values if `Subtract` is in play.
54 /// For example, doing Add(1) and Subtract(1) may lead to this
55 /// function returning -1 (wrapped).
56 /// With `Subtract`, consider using `NonNegativeRead` instead.
57 std::uintptr_t Read() const noexcept;
58
59 /// @brief Read the non-negative total counter value.
60 /// @note The read is done with `std::memory_order_acquire`.
61 ///
62 /// This is almost exactly like `Read`, but for an `Add-Subtract` race,
63 /// instead of returning a negative value, this function returns `0`. Consider
64 /// this a convenient shortcut to avoid seeing logically impossible negative
65 /// values.
66 std::uintptr_t NonNegativeRead() const noexcept;
67
68private:
69 struct Impl;
70 utils::FastPimpl<Impl, 32, 8> impl_;
71};
72
73} // namespace concurrent
74
75USERVER_NAMESPACE_END