Bond
 
Loading...
Searching...
No Matches
shared_counter.h
1// Copyright (c) Microsoft. All rights reserved.
2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
4#pragma once
5
6#include <bond/core/config.h>
7
8#include "capped_allocator_fwd.h"
9
10#include <boost/assert.hpp>
11#include <boost/intrusive_ptr.hpp>
12
13#include <atomic>
14#include <functional>
15
16namespace bond { namespace ext
17{
21 template <typename Counter>
23 {
24 public:
25 using value_type = typename Counter::value_type;
26
28 explicit shared_counter(value_type max_value)
29 : _value{ new internal_counter{ max_value } }
30 {}
31
35 template <typename Alloc>
36 shared_counter(value_type max_value, const Alloc& alloc)
37 : shared_counter{ allocate_counter(max_value, alloc) }
38 {}
39
40 bool try_add(value_type val) BOND_NOEXCEPT
41 {
42 return _value->try_add(val);
43 }
44
45 void subtract(value_type val) BOND_NOEXCEPT
46 {
47 _value->subtract(val);
48 }
49
50 value_type max_value() const BOND_NOEXCEPT
51 {
52 return _value->max_value();
53 }
54
55 value_type value() const BOND_NOEXCEPT
56 {
57 return _value->value();
58 }
59
60 private:
62 class internal_counter : public Counter
63 {
64 public:
65 using Counter::Counter;
66
67 friend void intrusive_ptr_add_ref(internal_counter* p) BOND_NOEXCEPT
68 {
69 ++p->_refs; // TODO: Use std::memory_order for atomic<T>
70 }
71
72 friend void intrusive_ptr_release(internal_counter* p)
73 {
74 if (--p->_refs == 0) // TODO: Use std::memory_order for atomic<T>
75 {
76 p->delete_this();
77 }
78 }
79
80 protected:
81 virtual ~internal_counter() = default;
82
84 virtual void delete_this()
85 {
86 delete this;
87 }
88
89 private:
90 typename std::conditional<
91 Counter::is_thread_safe::value,
92 std::atomic<value_type>,
93 value_type>::type _refs{};
94 };
95
96
98 template <typename Alloc>
99 class internal_counter_with_allocator : public internal_counter
100 {
101 using char_alloc = typename std::allocator_traits<Alloc>::template rebind_alloc<char>;
102
103 public:
107 template <typename C>
108 static internal_counter* allocate_counter(value_type max_value, capped_allocator<Alloc, C>& capped_alloc)
109 {
110 capped_allocator<char_alloc, C> capped_char_alloc{ capped_alloc };
111 return new (capped_char_alloc) // Allocate using capped allocator,
112 internal_counter_with_allocator{
113 max_value,
114 capped_char_alloc.get_allocator() }; // but store only the base one.
115 }
116
122 static void operator delete(void* /*ptr*/)
123 {
124 // This is a private type and we never allocate it with default "operator new".
125 // It is only allocated using the custom one that accepts a capped allocator.
126 BOOST_ASSERT(false);
127 }
128
129 private:
130 internal_counter_with_allocator(value_type max_value, const char_alloc& alloc)
131 : internal_counter{ max_value },
132 _alloc{ alloc }
133 {}
134
136 template <typename C>
137 static void* operator new(std::size_t size, capped_allocator<char_alloc, C>& alloc)
138 {
139 BOOST_ASSERT(size == sizeof(internal_counter_with_allocator));
140 return alloc.allocate(size);
141 }
142
146 template <typename C>
147 static void operator delete(void* ptr, capped_allocator<char_alloc, C>& alloc)
148 {
149 operator delete(ptr, alloc.get_allocator());
150 }
151
153 static void operator delete(void* ptr, char_alloc& alloc)
154 {
155 return alloc.deallocate(static_cast<char*>(ptr), sizeof(internal_counter_with_allocator));
156 }
157
159 void delete_this() override
160 {
161 auto alloc = _alloc; // Make a copy of the allocator so we can use it later.
162 this->~internal_counter_with_allocator();
163 operator delete(this, alloc);
164 }
165
166 char_alloc _alloc;
167 };
168
169
173 template <typename Alloc, typename C>
174 shared_counter(value_type max_value, capped_allocator<Alloc, C>& capped_alloc)
175 : _value{ internal_counter_with_allocator<Alloc>::allocate_counter(max_value, capped_alloc) }
176 {}
177
181 template <typename Alloc>
182 static shared_counter allocate_counter(value_type max_value, const Alloc& alloc)
183 {
184 single_threaded_counter<value_type> counter{ max_value };
185 capped_allocator<Alloc, decltype(counter)&> capped_alloc{ std::ref(counter), alloc };
187 max_value,
188 capped_alloc }; // Only a copy of underlying \c Alloc will be stored as part of
189 // the counter, so it is safe to have the \c counter on the stack.
190 shared_counter.try_add(counter.value());
191 return shared_counter;
192 }
193
194 boost::intrusive_ptr<internal_counter> _value;
195 };
196
197} } // namespace bond::ext
198
199namespace std
200{
201 template <typename Counter, typename Alloc>
202 struct uses_allocator<bond::ext::shared_counter<Counter>, Alloc> : std::true_type
203 {};
204
205} // namespace std
Shared counter to be used with capped_allocator.
Definition: shared_counter.h:23
shared_counter(value_type max_value, const Alloc &alloc)
Allocates counter using the provided allocator.
Definition: shared_counter.h:36
shared_counter(value_type max_value)
Constructs counter using default "operator new".
Definition: shared_counter.h:28
namespace bond
Definition: apply.h:17
STL namespace.