Metall  v0.29
A persistent memory allocator for data-centric analytics
fallback_allocator.hpp
Go to the documentation of this file.
1 // Copyright 2024 Lawrence Livermore National Security, LLC and other Metall
2 // Project Developers. See the top-level COPYRIGHT file for details.
3 //
4 // SPDX-License-Identifier: (Apache-2.0 OR MIT)
5 
6 #ifndef METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP
7 #define METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP
8 
9 #include <memory>
10 #include <cstdlib>
11 #include <type_traits>
12 
13 namespace metall::container {
14 
20 template <typename StatefulAllocator>
22  // Check if the StatefulAllocator takes arguments in its constructor
23  static_assert(!std::is_constructible<StatefulAllocator>::value,
24  "The stateful allocator must not be default constructible");
25 
26  private:
27  template <typename T>
28  using other_stateful_allocator_type = typename std::allocator_traits<
29  StatefulAllocator>::template rebind_alloc<T>;
30 
31  public:
32  // -------------------- //
33  // Public types and static values
34  // -------------------- //
35  using stateful_allocator_type = typename std::remove_const<
36  typename std::remove_reference<StatefulAllocator>::type>::type;
37 
38  using value_type = typename stateful_allocator_type::value_type;
39  using pointer = typename stateful_allocator_type::pointer;
40  using const_pointer = typename stateful_allocator_type::const_pointer;
41  using void_pointer = typename stateful_allocator_type::void_pointer;
43  typename stateful_allocator_type::const_void_pointer;
44  using difference_type = typename stateful_allocator_type::difference_type;
45  using size_type = typename stateful_allocator_type::size_type;
46 
48  template <typename T2>
49  struct rebind {
51  };
52 
53  public:
54  // -------------------- //
55  // Constructor & assign operator
56  // -------------------- //
57 
60  fallback_allocator_adaptor() noexcept : m_stateful_allocator(nullptr) {}
61 
64  template <
65  typename stateful_allocator_type2,
66  std::enable_if_t<std::is_constructible<stateful_allocator_type,
67  stateful_allocator_type2>::value,
68  int> = 0>
71  allocator_instance) noexcept
72  : m_stateful_allocator(allocator_instance.get_stateful_allocator()) {}
73 
76  template <
77  typename stateful_allocator_type2,
78  std::enable_if_t<std::is_constructible<stateful_allocator_type,
79  stateful_allocator_type2>::value,
80  int> = 0>
82  stateful_allocator_type2 allocator_instance) noexcept
83  : m_stateful_allocator(allocator_instance) {}
84 
87  default;
88 
91  default;
92 
95  const fallback_allocator_adaptor &) noexcept = default;
96 
99  template <
100  typename stateful_allocator_type2,
101  std::enable_if_t<std::is_constructible<stateful_allocator_type,
102  stateful_allocator_type2>::value,
103  int> = 0>
106  &other) noexcept {
107  m_stateful_allocator = other.stateful_allocator();
108  return *this;
109  }
110 
112  template <
113  typename stateful_allocator_type2,
114  std::enable_if_t<std::is_constructible<stateful_allocator_type,
115  stateful_allocator_type2>::value,
116  int> = 0>
118  const stateful_allocator_type2 &allocator_instance) noexcept {
119  m_stateful_allocator = allocator_instance;
120  return *this;
121  }
122 
125  fallback_allocator_adaptor &&other) noexcept = default;
126 
129  template <
130  typename stateful_allocator_type2,
131  std::enable_if_t<std::is_constructible<stateful_allocator_type,
132  stateful_allocator_type2>::value,
133  int> = 0>
136  m_stateful_allocator = std::move(other.stateful_allocator());
137  return *this;
138  }
139 
141  template <
142  typename stateful_allocator_type2,
143  std::enable_if_t<std::is_constructible<stateful_allocator_type,
144  stateful_allocator_type2>::value,
145  int> = 0>
147  stateful_allocator_type2 &&allocator_instance) noexcept {
148  m_stateful_allocator = std::move(allocator_instance);
149  return *this;
150  }
151 
155  pointer allocate(const size_type n) const {
156  if (priv_stateful_allocator_available()) {
157  return m_stateful_allocator.allocate(n);
158  }
159  return priv_fallback_allocate(n);
160  }
161 
165  void deallocate(pointer ptr, const size_type size) const {
166  if (priv_stateful_allocator_available()) {
167  m_stateful_allocator.deallocate(ptr, size);
168  } else {
169  priv_fallback_deallocate(ptr);
170  }
171  }
172 
175  size_type max_size() const noexcept {
176  return m_stateful_allocator.max_size();
177  }
178 
183  template <class... Args>
184  void construct(const pointer &ptr, Args &&...args) const {
185  if (priv_stateful_allocator_available()) {
186  m_stateful_allocator.construct(ptr, std::forward<Args>(args)...);
187  } else {
188  priv_fallback_construct(ptr, std::forward<Args>(args)...);
189  }
190  }
191 
194  void destroy(const pointer &ptr) const {
195  if (priv_stateful_allocator_available()) {
196  m_stateful_allocator.destroy(ptr);
197  } else {
198  priv_fallback_destroy(ptr);
199  }
200  }
201 
202  // ---------- This class's unique public functions ---------- //
203 
205  stateful_allocator_type &get_stateful_allocator() { return m_stateful_allocator; }
206 
209  return m_stateful_allocator;
210  }
211 
215  return priv_stateful_allocator_available();
216  }
217 
218  private:
219  // -------------------- //
220  // Private methods
221  // -------------------- //
222  auto priv_stateful_allocator_available() const {
223  return !!(m_stateful_allocator.get_pointer_to_manager_kernel());
224  }
225 
226  pointer priv_fallback_allocate(const size_type n) const {
227  if (max_size() < n) {
228  throw std::bad_array_new_length();
229  }
230 
231  void *const addr = std::malloc(n * sizeof(value_type));
232  if (!addr) {
233  throw std::bad_alloc();
234  }
235 
236  return pointer(static_cast<value_type *>(addr));
237  }
238 
239  void priv_fallback_deallocate(pointer ptr) const {
240  std::free(to_raw_pointer(ptr));
241  }
242 
243  void priv_fallback_destroy(pointer ptr) const { (*ptr).~value_type(); }
244 
245  template <class... arg_types>
246  void priv_fallback_construct(const pointer &ptr, arg_types &&...args) const {
247  ::new ((void *)to_raw_pointer(ptr))
248  value_type(std::forward<arg_types>(args)...);
249  }
250 
251  // -------------------- //
252  // Private fields
253  // -------------------- //
254  stateful_allocator_type m_stateful_allocator;
255 };
256 
257 template <typename stateful_allocator_type>
258 inline bool operator==(
261  // Return true if they point to the same manager kernel
262  return rhd.get_stateful_allocator() == lhd.get_stateful_allocator();
263 }
264 
265 template <typename stateful_allocator_type>
266 inline bool operator!=(
269  return !(rhd == lhd);
270 }
271 
272 } // namespace metall::container
273 
276 
277 #endif // METALL_CONTAINER_FALLBACK_ALLOCATOR_HPP
A Metall STL compatible allocator which fallbacks to a heap allocator (e.g., malloc()) if its constru...
Definition: fallback_allocator.hpp:21
void construct(const pointer &ptr, Args &&...args) const
Constructs an object of T.
Definition: fallback_allocator.hpp:184
typename stateful_allocator_type::size_type size_type
Definition: fallback_allocator.hpp:45
stateful_allocator_type & get_stateful_allocator()
Returns a reference to the stateful allocator.
Definition: fallback_allocator.hpp:205
typename stateful_allocator_type::const_void_pointer const_void_pointer
Definition: fallback_allocator.hpp:43
fallback_allocator_adaptor & operator=(const fallback_allocator_adaptor &) noexcept=default
Copy assign operator.
fallback_allocator_adaptor(fallback_allocator_adaptor< stateful_allocator_type2 > allocator_instance) noexcept
Construct a new instance using an instance of fallback_allocator_adaptor with any stateful_allocator ...
Definition: fallback_allocator.hpp:69
typename std::remove_const< typename std::remove_reference< StatefulAllocator >::type >::type stateful_allocator_type
Definition: fallback_allocator.hpp:36
void destroy(const pointer &ptr) const
Deconstruct an object of T.
Definition: fallback_allocator.hpp:194
fallback_allocator_adaptor & operator=(fallback_allocator_adaptor< stateful_allocator_type2 > &&other) noexcept
Move assign operator, using an instance of fallback_allocator_adaptor with any stateful_allocator typ...
Definition: fallback_allocator.hpp:134
typename stateful_allocator_type::pointer pointer
Definition: fallback_allocator.hpp:39
pointer allocate(const size_type n) const
Allocates n * sizeof(T) bytes of storage.
Definition: fallback_allocator.hpp:155
typename stateful_allocator_type::difference_type difference_type
Definition: fallback_allocator.hpp:44
fallback_allocator_adaptor(const fallback_allocator_adaptor &other) noexcept=default
Copy constructor.
fallback_allocator_adaptor & operator=(stateful_allocator_type2 &&allocator_instance) noexcept
Move assign operator for any stateful_allocator.
Definition: fallback_allocator.hpp:146
size_type max_size() const noexcept
The size of the theoretical maximum allocation size.
Definition: fallback_allocator.hpp:175
fallback_allocator_adaptor & operator=(const stateful_allocator_type2 &allocator_instance) noexcept
Copy assign operator for any stateful_allocator.
Definition: fallback_allocator.hpp:117
typename stateful_allocator_type::void_pointer void_pointer
Definition: fallback_allocator.hpp:41
const stateful_allocator_type & get_stateful_allocator() const
Returns a const reference to the stateful allocator.
Definition: fallback_allocator.hpp:208
fallback_allocator_adaptor(fallback_allocator_adaptor &&other) noexcept=default
Move constructor.
typename stateful_allocator_type::value_type value_type
Definition: fallback_allocator.hpp:38
fallback_allocator_adaptor & operator=(const fallback_allocator_adaptor< stateful_allocator_type2 > &other) noexcept
Copy assign operator, using an instance of fallback_allocator_adaptor with any stateful_allocator typ...
Definition: fallback_allocator.hpp:104
bool stateful_allocator_available() const
Returns true if the stateful allocator is available.
Definition: fallback_allocator.hpp:214
fallback_allocator_adaptor() noexcept
Default constructor which falls back on the regular allocator (i.e., malloc()).
Definition: fallback_allocator.hpp:60
fallback_allocator_adaptor & operator=(fallback_allocator_adaptor &&other) noexcept=default
Move assign operator.
fallback_allocator_adaptor(stateful_allocator_type2 allocator_instance) noexcept
Construct a new instance using an instance of any stateful_allocator.
Definition: fallback_allocator.hpp:81
void deallocate(pointer ptr, const size_type size) const
Deallocates the storage reference by the pointer ptr.
Definition: fallback_allocator.hpp:165
typename stateful_allocator_type::const_pointer const_pointer
Definition: fallback_allocator.hpp:40
Namespace for Metall container.
bool operator!=(const fallback_allocator_adaptor< stateful_allocator_type > &rhd, const fallback_allocator_adaptor< stateful_allocator_type > &lhd)
Definition: fallback_allocator.hpp:266
bool operator==(const fallback_allocator_adaptor< stateful_allocator_type > &rhd, const fallback_allocator_adaptor< stateful_allocator_type > &lhd)
Definition: fallback_allocator.hpp:258
Makes another allocator type for type T2.
Definition: fallback_allocator.hpp:49