DASH  0.3.0
polymorphic_allocator.h
1 /* polymorphic_allocator.h -*-C++-*-
2  *
3  * Copyright 2017 Pablo Halpern.
4  * Distributed under the Boost Software License, Version 1.0.
5  * (See accompanying file LICENSE_1_0.txt or copy at
6  * http://www.boost.org/LICENSE_1_0.txt)
7  */
8 
9 #ifndef INCLUDED_POLYMORPHIC_ALLOCATOR_DOT_H
10 #define INCLUDED_POLYMORPHIC_ALLOCATOR_DOT_H
11 
12 #if __cpp_lib_memory_resource < 201603
13 
14 #include <atomic>
15 #include <cstddef> // For max_align_t
16 #include <memory>
17 #include <new>
18 #include <scoped_allocator>
19 
20 #include <cpp17/cstddef.h>
21 
22 namespace std {
23 
24 namespace pmr {
25 
26 // Abstract base class for allocator resources.
27 // Conforms to the C++17 standard, section [mem.res.class].
29  static constexpr size_t max_align = alignof(max_align_t);
30 
31  static std::atomic<memory_resource*> s_default_resource;
32 
33  friend memory_resource* set_default_resource(memory_resource* /*r*/);
34  friend memory_resource* get_default_resource();
35 
36 public:
37  virtual ~memory_resource();
38 
39  void* allocate(size_t bytes, size_t alignment = max_align)
40  {
41  return do_allocate(bytes, alignment);
42  }
43  void deallocate(void* p, size_t bytes, size_t alignment = max_align)
44  {
45  return do_deallocate(p, bytes, alignment);
46  }
47 
48  // `is_equal` is needed because polymorphic allocators are sometimes
49  // produced as a result of type erasure. In that case, two different
50  // instances of a polymorphic_memory_resource may actually represent
51  // the same underlying allocator and should compare equal, even though
52  // their addresses are different.
53  bool is_equal(const memory_resource& other) const noexcept
54  {
55  return do_is_equal(other);
56  }
57 
58 protected:
59  virtual void* do_allocate(size_t bytes, size_t alignment) = 0;
60  virtual void do_deallocate(void* p, size_t bytes, size_t alignment) = 0;
61  virtual bool do_is_equal(const memory_resource& other) const noexcept = 0;
62 };
63 
64 inline bool operator==(const memory_resource& a, const memory_resource& b)
65 {
66  // Call `is_equal` rather than using address comparisons because some
67  // polymorphic allocators are produced as a result of type erasure. In
68  // that case, `a` and `b` may contain `memory_resource`s with different
69  // addresses which, nevertheless, should compare equal.
70  return &a == &b || a.is_equal(b);
71 }
72 
73 inline bool operator!=(const memory_resource& a, const memory_resource& b)
74 {
75  return !(a == b);
76 }
77 
78 namespace __details {
79 
80 // STL allocator that holds a pointer to a polymorphic allocator resource.
81 // Used to implement `polymorphic_allocator`, which is a scoped allocator.
82 template <class Tp>
84  memory_resource* m_resource;
85 
86 public:
87  using value_type = Tp;
88 
89  // These types are old-fashioned, pre-C++11 requirements, still needed by
90  // g++'s `basic_string` implementation.
91  using size_type = size_t;
92  using difference_type = ptrdiff_t;
93  using reference = Tp&;
94  using const_reference = Tp const&;
95  using pointer = Tp*;
96  using const_pointer = Tp const*;
97 
100 
101  template <class U>
102  explicit polymorphic_allocator_imp(
103  const polymorphic_allocator_imp<U>& other);
104 
105  Tp* allocate(size_t n);
106  void deallocate(Tp* p, size_t n);
107 
108  // Return a default-constructed allocator
109  polymorphic_allocator_imp select_on_container_copy_construction() const;
110 
111  memory_resource* resource() const;
112 };
113 
114 template <class T1, class T2>
115 bool operator==(
118 
119 template <class T1, class T2>
120 bool operator!=(
123 
124 template <size_t Align>
126 
127 template <>
128 struct aligned_chunk<1> {
129  char x;
130 };
131 template <>
132 struct aligned_chunk<2> {
133  short x;
134 };
135 template <>
136 struct aligned_chunk<4> {
137  int x;
138 };
139 template <>
140 struct aligned_chunk<8> {
141  long long x;
142 };
143 template <>
144 struct aligned_chunk<16> {
145  __attribute__((aligned(16))) char x;
146 };
147 template <>
148 struct aligned_chunk<32> {
149  __attribute__((aligned(32))) char x;
150 };
151 template <>
152 struct aligned_chunk<64> {
153  __attribute__((aligned(64))) char x;
154 };
155 
156 // Adaptor to make a polymorphic allocator resource type from an STL allocator
157 // type. This is really a C++20 feature, but it's useful for implementing
158 // this component.
159 template <class Allocator>
161  typename std::allocator_traits<Allocator>::template rebind_alloc<
162  max_align_t>
163  m_alloc;
164 
165  template <size_t Align>
166  void* allocate_imp(size_t bytes);
167 
168  template <size_t Align>
169  void deallocate_imp(void* p, size_t bytes);
170 
171 public:
172  typedef Allocator allocator_type;
173 
174  resource_adaptor_imp() = default;
175 
176  resource_adaptor_imp(const resource_adaptor_imp&) = default;
177 
178  template <class Allocator2>
179  explicit resource_adaptor_imp(
180  Allocator2&& a2,
181  typename std::enable_if<
182  std::is_convertible<Allocator2, Allocator>::value,
183  int>::type /*unused*/
184  = 0);
185 
186 protected:
187  void* do_allocate(size_t bytes, size_t alignment = 0) override;
188  void do_deallocate(void* p, size_t bytes, size_t alignment = 0) override;
189 
190  bool do_is_equal(const memory_resource& other) const noexcept override;
191 
192  allocator_type get_allocator() const
193  {
194  return m_alloc;
195  }
196 };
197 
198 } // end namespace __details
199 
200 // A resource_adaptor converts a traditional STL allocator to a polymorphic
201 // memory resource. Somehow, this didn't make it into C++17, but it should
202 // have, so here it is.
203 // This alias ensures that `resource_adaptor<T>` and
204 // `resource_adaptor<U>` are always the same type, whether or not
205 // `T` and `U` are the same type.
206 template <class Allocator>
208  typename std::allocator_traits<Allocator>::template rebind_alloc<byte>>;
209 
210 // Memory resource that uses new and delete.
211 class new_delete_resource : public resource_adaptor<std::allocator<byte>> {
212 };
213 
214 // Return a pointer to a global instance of `new_delete_resource`.
215 new_delete_resource* new_delete_resource_singleton();
216 
217 // Get the current default resource
218 memory_resource* get_default_resource();
219 
220 // Set the default resource
221 memory_resource* set_default_resource(memory_resource* r);
222 
223 template <class Tp>
224 class polymorphic_allocator : public std::scoped_allocator_adaptor<
225  __details::polymorphic_allocator_imp<Tp>> {
227  typedef std::scoped_allocator_adaptor<Imp> Base;
228 
229 public:
230  // g++-4.6.3 does not use allocator_traits in shared_ptr, so we have to
231  // provide an explicit rebind.
232  template <typename U>
233  struct rebind {
235  };
236 
237  polymorphic_allocator() = default;
239  : Base(Imp(r))
240  {
241  }
242 
243  template <class U>
245  : Base(Imp((other.resource())))
246  {
247  }
248 
249  template <class U>
250  explicit polymorphic_allocator(
252  : Base(other)
253  {
254  }
255 
256  // Return a default-constructed allocator
257  polymorphic_allocator select_on_container_copy_construction() const
258  {
259  return polymorphic_allocator();
260  }
261 
262  memory_resource* resource() const
263  {
264  return this->outer_allocator().resource();
265  }
266 };
267 
268 template <class T1, class T2>
269 inline bool operator==(
271 {
272  return a.outer_allocator() == b.outer_allocator();
273 }
274 
275 template <class T1, class T2>
276 inline bool operator!=(
278 {
279  return !(a == b);
280 }
281 
282 } // end namespace pmr
283 
285 // INLINE AND TEMPLATE FUNCTION IMPLEMENTATIONS
287 
288 inline pmr::memory_resource::~memory_resource() = default;
289 
290 inline pmr::memory_resource* pmr::get_default_resource()
291 {
292  memory_resource* ret = pmr::memory_resource::s_default_resource.load();
293  if (nullptr == ret) {
294  ret = new_delete_resource_singleton();
295  }
296  return ret;
297 }
298 
299 inline pmr::memory_resource* pmr::set_default_resource(
301 {
302  if (nullptr == r) {
303  r = new_delete_resource_singleton();
304  }
305 
306  // TBD, should use an atomic swap
307  pmr::memory_resource* prev = get_default_resource();
308  pmr::memory_resource::s_default_resource.store(r);
309  return prev;
310 }
311 
312 template <class Allocator>
313 template <class Allocator2>
315  Allocator2&& a2,
316  typename std::enable_if<
317  std::is_convertible<Allocator2, Allocator>::value,
318  int>::type /*unused*/)
319  : m_alloc(std::forward<Allocator2>(a2))
320 {
321 }
322 
323 template <class Allocator>
324 template <size_t Align>
326  size_t bytes)
327 {
328  typedef __details::aligned_chunk<Align> chunk;
329  size_t chunks = (bytes + Align - 1) / Align;
330 
331  typedef
332  typename std::allocator_traits<Allocator>::template rebind_traits<chunk>
333  chunk_traits;
334  typename chunk_traits::allocator_type rebound(m_alloc);
335  return chunk_traits::allocate(rebound, chunks);
336 }
337 
338 template <class Allocator>
339 template <size_t Align>
341  void* p, size_t bytes)
342 {
343  typedef __details::aligned_chunk<Align> chunk;
344  size_t chunks = (bytes + Align - 1) / Align;
345 
346  typedef
347  typename std::allocator_traits<Allocator>::template rebind_traits<chunk>
348  chunk_traits;
349  typename chunk_traits::allocator_type rebound(m_alloc);
350  return chunk_traits::deallocate(rebound, static_cast<chunk*>(p), chunks);
351 }
352 
353 template <class Allocator>
355  size_t bytes, size_t alignment)
356 {
357  static const size_t max_natural_alignment = sizeof(max_align_t);
358 
359  if (0 == alignment) {
360  // Choose natural alignment for `bytes`
361  alignment = ((bytes ^ (bytes - 1)) >> 1) + 1;
362  if (alignment > max_natural_alignment) {
363  alignment = max_natural_alignment;
364  }
365  }
366 
367  switch (alignment) {
368  case 1:
369  return allocate_imp<1>(bytes);
370  case 2:
371  return allocate_imp<2>(bytes);
372  case 4:
373  return allocate_imp<4>(bytes);
374  case 8:
375  return allocate_imp<8>(bytes);
376  case 16:
377  return allocate_imp<16>(bytes);
378  case 32:
379  return allocate_imp<32>(bytes);
380  case 64:
381  return allocate_imp<64>(bytes);
382  default: {
383  size_t chunks = (bytes + sizeof(void*) + alignment - 1) / 64;
384  size_t chunkbytes = chunks * 64;
385  void* original = allocate_imp<64>(chunkbytes);
386 
387  // Make room for original pointer storage
388  char* p = static_cast<char*>(original) + sizeof(void*);
389 
390  // Round up to nearest alignment boundary
391  p += alignment - 1;
392  p -= (size_t(p)) & (alignment - 1);
393 
394  // Store original pointer in word before allocated pointer
395  reinterpret_cast<void**>(p)[-1] = original;
396 
397  return p;
398  }
399  }
400 }
401 
402 template <class Allocator>
404  void* p, size_t bytes, size_t alignment)
405 {
406  static const size_t max_natural_alignment = sizeof(max_align_t);
407 
408  if (0 == alignment) {
409  // Choose natural alignment for `bytes`
410  alignment = ((bytes ^ (bytes - 1)) >> 1) + 1;
411  if (alignment > max_natural_alignment) {
412  alignment = max_natural_alignment;
413  }
414  }
415 
416  switch (alignment) {
417  case 1:
418  deallocate_imp<1>(p, bytes);
419  break;
420  case 2:
421  deallocate_imp<2>(p, bytes);
422  break;
423  case 4:
424  deallocate_imp<4>(p, bytes);
425  break;
426  case 8:
427  deallocate_imp<8>(p, bytes);
428  break;
429  case 16:
430  deallocate_imp<16>(p, bytes);
431  break;
432  case 32:
433  deallocate_imp<32>(p, bytes);
434  break;
435  case 64:
436  deallocate_imp<64>(p, bytes);
437  break;
438  default: {
439  size_t chunks = (bytes + sizeof(void*) + alignment - 1) / 64;
440  size_t chunkbytes = chunks * 64;
441  void* original = reinterpret_cast<void**>(p)[-1];
442 
443  deallocate_imp<64>(original, chunkbytes);
444  }
445  }
446 }
447 
448 template <class Allocator>
450  const memory_resource& other) const noexcept
451 {
452  const auto* other_p = dynamic_cast<const resource_adaptor_imp*>(&other);
453 
454  if (other_p) {
455  return this->m_alloc == other_p->m_alloc;
456  }
457  else {
458  return false;
459  }
460 }
461 
462 namespace __pmrd = pmr::__details;
463 
464 template <class Tp>
466  : m_resource(get_default_resource())
467 {
468 }
469 
470 template <class Tp>
473  : m_resource(r != nullptr ? r : get_default_resource())
474 {
475 }
476 
477 template <class Tp>
478 template <class U>
481  : m_resource(other.resource())
482 {
483 }
484 
485 template <class Tp>
487 {
488  return static_cast<Tp*>(m_resource->allocate(n * sizeof(Tp), alignof(Tp)));
489 }
490 
491 template <class Tp>
492 inline void __pmrd::polymorphic_allocator_imp<Tp>::deallocate(Tp* p, size_t n)
493 {
494  m_resource->deallocate(p, n * sizeof(Tp), alignof(Tp));
495 }
496 
497 template <class Tp>
500  const
501 {
503 }
504 
505 template <class Tp>
507  const
508 {
509  return m_resource;
510 }
511 
512 template <class T1, class T2>
513 inline bool __pmrd::operator==(
516 {
517  // `operator==` for `memory_resource` first checks for equality of
518  // addresses and calls `is_equal` only if the addresses differ. The call
519  // `is_equal` because some polymorphic allocators are produced as a result
520  // of type erasure. In that case, `a` and `b` may contain
521  // `memory_resource`s with different addresses which, nevertheless,
522  // should compare equal.
523  return *a.resource() == *b.resource();
524 }
525 
526 template <class T1, class T2>
527 inline bool __pmrd::operator!=(
530 {
531  return *a.resource() != *b.resource();
532 }
533 
534 } // namespace std
535 
536 #endif
537 #endif // ! defined(INCLUDED_POLYMORPHIC_ALLOCATOR_DOT_H)
static __attribute__((unused)) dart_ret_t dart_gptr_incaddr(dart_gptr_t *gptr
Add &#39;offs&#39; to the address specified by the global pointer.
see https://en.cppreference.com/w/cpp/feature_test for recommended feature tests
Definition: cstddef.h:8