Allocator Builder
Policy Based C++ Template Allocator Library
 All Classes Functions Variables Enumerations Enumerator Groups Pages
cascading_allocator.hpp
1 //
3 // Copyright 2014 Felix Petriconi
4 //
5 // License: http://boost.org/LICENSE_1_0.txt, Boost License 1.0
6 //
7 // Authors: http://petriconi.net, Felix Petriconi
8 //
10 #pragma once
11 
12 #include "allocator_base.hpp"
13 #include "internal/noatomic.hpp"
14 #include "internal/reallocator.hpp"
15 #include <atomic>
16 #include <cassert>
17 
18 namespace alb {
19  inline namespace v_100 {
29  template <bool Shared, typename Allocator> class cascading_allocator_base {
30  struct Node;
31  using NodePtr =
33 
34  struct Node {
35  Node() noexcept
36  : next{ nullptr }
37  , allocatedThisSize{ 0 }
38  {
39  }
40 
41  Node(Node &&x) noexcept
42  {
43  *this = std::move(x);
44  }
45 
46  Node &operator=(Node &&x) noexcept
47  {
48  allocator = std::move(x.allocator);
49  next = x.next.load();
50  allocatedThisSize = x.allocatedThisSize;
51 
52  x.next = nullptr;
53  x.allocatedThisSize = 0;
54 
55  return *this;
56  }
57 
58  Allocator allocator;
59  NodePtr next;
60  size_t allocatedThisSize;
61  };
62 
63  NodePtr root_;
64 
65  block allocate_no_grow(size_t n) noexcept
66  {
67  block result;
68  auto p = root_.load();
69  while (p) {
70  result = p->allocator.allocate(n);
71  if (result) {
72  return result;
73  }
74  if (!p->next.load()) {
75  break;
76  }
77  p = p->next.load();
78  }
79  return result;
80  }
81 
82  Node *create_node() noexcept
83  {
84  // Create a temporary node with an allocator on the stack
85  Node nodeOnStack;
86 
87  // Use this allocator to create the first node in allocators space
88  auto nodeBlock = nodeOnStack.allocator.allocate(sizeof(Node));
89 
90  nodeOnStack.allocatedThisSize = nodeBlock.length;
91  auto result = static_cast<Node *>(nodeBlock.ptr);
92 
93  if (!result) {
94  return nullptr;
95  }
96 
97  // Create a new Node emplace
98  new (result) Node();
99 
100  // Move the node from the stack to the allocated space
101  *result = std::move(nodeOnStack);
102 
103  return result;
104  }
105 
109  void erase_node(Node *n) noexcept
110  {
111  if (n == nullptr) {
112  return;
113  }
114  if (n->next.load()) {
115  // delete all possible next Nodes in the list
116  erase_node(n->next.load());
117  n->next = nullptr;
118  }
119  // Create a temporary node on the stack
120  Node stackNode;
121 
122  // Move the allocator to the temporary node
123  stackNode = std::move(*n);
124  block allocatedBlock(n, stackNode.allocatedThisSize);
125 
126  stackNode.allocator.deallocate(allocatedBlock);
127  }
128 
129  void shrink() noexcept
130  {
131  erase_node(root_.load());
132  }
133 
134  Node *find_owning_node(const block &b) const noexcept
135  {
136  auto p = root_.load();
137  while (p) {
138  if (p->allocator.owns(b)) {
139  return p;
140  }
141  p = p->next.load();
142  }
143  return nullptr;
144  }
145 
147  cascading_allocator_base &operator=(const cascading_allocator_base &) = delete;
148 
149  public:
150  using allocator = Allocator;
151 
152  static constexpr bool supports_truncated_deallocation = Allocator::supports_truncated_deallocation;
153  static constexpr unsigned alignment = Allocator::alignment;
154 
155  cascading_allocator_base() noexcept
156  : root_(nullptr)
157  {
158  }
159 
160  static constexpr size_t good_size(size_t n) {
161  return Allocator::good_size(n);
162  }
163 
165  {
166  *this = std::move(x);
167  }
168 
169  cascading_allocator_base &operator=(cascading_allocator_base &&x) noexcept
170  {
171  if (this == &x) {
172  return *this;
173  }
174  shrink();
175  root_ = std::move(x.root_);
176  x.root_ = nullptr;
177  return *this;
178  }
179 
184  {
185  shrink();
186  }
187 
192  block allocate(size_t n) noexcept
193  {
194  if (n == 0) {
195  return{};
196  }
197 
198  block result = allocate_no_grow(n);
199  if (result) {
200  return result;
201  }
202 
203  // no node at all there
204  if (root_.load() == nullptr) {
205  auto firstNode = create_node();
206  Node *nullNode = nullptr;
207  // test if in the meantime someone else has created a node
208  if (!root_.compare_exchange_weak(nullNode, firstNode)) {
209  erase_node(firstNode);
210  }
211 
212  result = allocate_no_grow(n);
213  if (result) {
214  return result;
215  }
216  }
217 
218  // a new node must be appended
219  auto newNode = create_node();
220  Node *nullNode = nullptr;
221  auto p = root_.load();
222  do {
223  p = root_;
224  while (p->next.load() != nullptr) {
225  p = p->next;
226  }
227  } while (!p->next.compare_exchange_weak(nullNode, newNode));
228 
229  result = allocate_no_grow(n);
230  return result;
231  }
232 
236  void deallocate(block &b) noexcept
237  {
238  if (!b) {
239  return;
240  }
241 
242  if (!owns(b)) {
243  assert(!"It is not wise to let me deallocate a foreign Block!");
244  return;
245  }
246 
247  auto p = find_owning_node(b);
248  if (p != nullptr) {
249  p->allocator.deallocate(b);
250  }
251  }
252 
261  bool reallocate(block &b, size_t n) noexcept
262  {
263  if (internal::is_reallocation_handled_default(*this, b, n)) {
264  return true;
265  }
266 
267  auto p = find_owning_node(b);
268  if (p == nullptr) {
269  return false;
270  }
271 
272  if (p->allocator.reallocate(b, n)) {
273  return true;
274  }
275 
276  return internal::reallocate_with_copy(*this, *this, b, n);
277  }
278 
286  template<typename U = Allocator>
287  typename std::enable_if<traits::has_expand<U>::value, bool>::type
288  expand(block &b, size_t delta) noexcept
289  {
290  auto p = find_owning_node(b);
291  if (p == nullptr) {
292  return false;
293  }
294  return p->allocator.expand(b, delta);
295  }
296 
302  bool owns(const block &b) const noexcept
303  {
304  return find_owning_node(b) != nullptr;
305  }
306 
313  template <typename U = Allocator>
314  typename std::enable_if<traits::has_deallocate_all<U>::value, void>::type
315  deallocate_all() noexcept
316  {
317  shrink();
318  }
319  };
320 
328  template <class Allocator>
329  class shared_cascading_allocator : public cascading_allocator_base<true, Allocator> {
330  public:
331  shared_cascading_allocator() noexcept
332  {
333  }
334  };
335 
343  template <class Allocator>
344  class cascading_allocator : public cascading_allocator_base<false, Allocator> {
345  public:
346  cascading_allocator() noexcept
347  {
348  }
349  };
350  }
351  using namespace v_100;
352 }
std::enable_if< traits::has_deallocate_all< U >::value, void >::type deallocate_all() noexcept
bool reallocate(block &b, size_t n) noexcept
bool reallocate_with_copy(OldAllocator &oldAllocator, NewAllocator &newAllocator, block &b, size_t n) noexcept
Definition: reallocator.hpp:35
bool owns(const block &b) const noexcept
std::enable_if< traits::has_expand< U >::value, bool >::type expand(block &b, size_t delta) noexcept