userver: userver/concurrent/striped_counter.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 {
26 public:
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
68 private:
69 struct Impl;
70 utils::FastPimpl<Impl, 32, 8> impl_;
71};
72
73} // namespace concurrent
74
75USERVER_NAMESPACE_END