sandbox
Loading...
Searching...
No Matches
counting_resource.hpp
1#ifndef LIBSBX_MEMORY_COUNTING_RESOURCE_HPP_
2#define LIBSBX_MEMORY_COUNTING_RESOURCE_HPP_
3
4#include <memory_resource>
5#include <atomic>
6#include <cstddef>
7#include <algorithm>
8
9namespace sbx::memory {
10
11template<typename Type>
12struct is_atomic : std::false_type {};
13
14template<typename Type>
15struct is_atomic<std::atomic<Type>> : std::true_type {};
16
17template<typename Type>
18inline constexpr bool is_atomic_v = is_atomic<Type>::value;
19
20template<typename Counter>
21struct counter_value;
22
23template<typename Type>
24struct counter_value<std::atomic<Type>> { using type = Type; };
25
26template<typename Type>
27struct counter_value { using type = Type; };
28
29template<typename Counter>
30using counter_value_t = typename counter_value<Counter>::type;
31
32namespace detail {
33
34template<typename Counter>
35constexpr auto load(const Counter& counter) noexcept -> counter_value_t<Counter> {
36 if constexpr (is_atomic_v<Counter>) {
37 return counter.load(std::memory_order_relaxed);
38 } else {
39 return counter;
40 }
41}
42
43template<typename Counter>
44constexpr void store(Counter& counter, counter_value_t<Counter> value) noexcept {
45 if constexpr (is_atomic_v<Counter>) {
46 counter.store(value, std::memory_order_relaxed);
47 } else {
48 counter = value;
49 }
50}
51
52template<typename Counter>
53constexpr auto add(Counter& counter, counter_value_t<Counter> value) noexcept -> counter_value_t<Counter> {
54 if constexpr (is_atomic_v<Counter>) {
55 return counter.fetch_add(value, std::memory_order_relaxed) + value;
56 } else {
57 counter += value;
58
59 return counter;
60 }
61}
62
63template<typename Counter>
64constexpr void subtract(Counter& counter, counter_value_t<Counter> value) noexcept {
65 if constexpr (is_atomic_v<Counter>) {
66 counter.fetch_sub(value, std::memory_order_relaxed);
67 } else {
68 counter -= value;
69 }
70}
71
72template<typename Counter>
73constexpr void increment(Counter& counter) noexcept {
74 if constexpr (is_atomic_v<Counter>) {
75 counter.fetch_add(1, std::memory_order_relaxed);
76 } else {
77 ++counter;
78 }
79}
80
81template<typename Counter>
82constexpr void update_peak(Counter& peak, counter_value_t<Counter> now) noexcept {
83 if constexpr (is_atomic_v<Counter>) {
84 auto current = peak.load(std::memory_order_relaxed);
85
86 while (now > current && !peak.compare_exchange_weak(current, now, std::memory_order_relaxed, std::memory_order_relaxed)) { }
87 } else {
88 if (now > peak) {
89 peak = now;
90 }
91 }
92}
93
94} // namespace detail
95
96template<typename Counter>
97requires (std::is_unsigned_v<counter_value_t<Counter>>)
98class basic_counting_resource final : public std::pmr::memory_resource {
99
100public:
101
102 using counter_type = Counter;
103 using value_type = counter_value_t<counter_type>;
104
105 explicit basic_counting_resource(std::pmr::memory_resource* upstream)
106 : _upstream{upstream} {}
107
108 void reset() noexcept {
109 detail::store(_bytes_outstanding, 0);
110 detail::store(_peak_outstanding, 0);
111 detail::store(_alloc_count, 0);
112 detail::store(_dealloc_count, 0);
113 }
114
115 auto outstanding() const noexcept -> value_type {
116 return detail::load(_bytes_outstanding);
117 }
118
119 auto peak() const noexcept -> value_type {
120 return detail::load(_peak_outstanding);
121 }
122
123 auto allocs() const noexcept -> value_type {
124 return detail::load(_alloc_count);
125 }
126
127 auto deallocs() const noexcept -> value_type {
128 return detail::load(_dealloc_count);
129 }
130
131private:
132
133 auto do_allocate(std::size_t bytes, std::size_t alignment) -> void* override {
134 void* ptr = _upstream->allocate(bytes, alignment);
135
136 detail::increment(_alloc_count);
137
138 const auto count = static_cast<value_type>(bytes);
139 const auto now = detail::add(_bytes_outstanding, count);
140
141 detail::update_peak(_peak_outstanding, now);
142
143 return ptr;
144 }
145
146 auto do_deallocate(void* ptr, std::size_t bytes, std::size_t alignment) -> void override {
147 detail::increment(_dealloc_count);
148 detail::subtract(_bytes_outstanding, static_cast<value_type>(bytes));
149
150 _upstream->deallocate(ptr, bytes, alignment);
151 }
152
153 auto do_is_equal(const std::pmr::memory_resource& other) const noexcept -> bool override {
154 return this == &other;
155 }
156
157 std::pmr::memory_resource* _upstream;
158
159 counter_type _bytes_outstanding{0};
160 counter_type _peak_outstanding{0};
161 counter_type _alloc_count{0};
162 counter_type _dealloc_count{0};
163
164}; // struct counting_resource
165
166template<std::unsigned_integral Type>
168
171
172} // namespace sbx::memory
173
174#endif // LIBSBX_MEMORY_COUNTING_RESOURCE_HPP_
Definition: counting_resource.hpp:98
Definition: counting_resource.hpp:27
Definition: counting_resource.hpp:12