Metall  v0.28
A persistent memory allocator for data-centric analytics
stl_allocator.hpp
Go to the documentation of this file.
1 // Copyright 2020 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_STL_ALLOCATOR_HPP
7 #define METALL_STL_ALLOCATOR_HPP
8 
9 #include <memory>
10 #include <type_traits>
11 #include <cassert>
12 #include <limits>
13 #include <new>
14 
15 #include <metall/offset_ptr.hpp>
16 #include <metall/logger.hpp>
17 
18 namespace metall {
19 
33 template <typename T, typename metall_manager_kernel_type>
35  public:
36  // -------------------- //
37  // Public types and static values
38  // -------------------- //
39  using value_type = T;
40  using pointer = typename std::pointer_traits<
41  typename metall_manager_kernel_type::void_pointer>::
42  template rebind<value_type>;
43  using const_pointer =
44  typename std::pointer_traits<pointer>::template rebind<const value_type>;
45  using void_pointer =
46  typename std::pointer_traits<pointer>::template rebind<void>;
48  typename std::pointer_traits<pointer>::template rebind<const void>;
50  typename std::pointer_traits<pointer>::difference_type;
51  using size_type = typename std::make_unsigned<difference_type>::type;
52  using manager_kernel_type = metall_manager_kernel_type;
53 
56  template <typename T2>
57  struct rebind {
59  };
60 
61  public:
62  // -------------------- //
63  // Constructor & assign operator
64  // -------------------- //
65  // Following manager.hpp in Boost.interprocess, 'explicit' keyword is not used
66  // on purpose although this allocator won't work correctly w/o a valid
67  // manager_kernel_address. The following code will work:
68  //
69  // void func(stl_allocator<int, manager_kernel_type>) {...}
70  // int main() {
71  // manager_kernel_type** ptr = ...
72  // func(ptr); // OK
73  // }
74  //
76  manager_kernel_type *const *const pointer_manager_kernel_address) noexcept
77  : m_ptr_manager_kernel_address(pointer_manager_kernel_address) {}
78 
80  template <typename T2>
82  stl_allocator<T2, manager_kernel_type> allocator_instance) noexcept
83  : m_ptr_manager_kernel_address(
84  allocator_instance.get_pointer_to_manager_kernel()) {}
85 
87  stl_allocator(const stl_allocator &other) noexcept = default;
88 
90  stl_allocator(stl_allocator &&other) noexcept = default;
91 
93  ~stl_allocator() noexcept = default;
94 
96  stl_allocator &operator=(const stl_allocator &) noexcept = default;
97 
99  template <typename T2>
100  stl_allocator &operator=(
101  const stl_allocator<T2, manager_kernel_type> &other) noexcept {
102  m_ptr_manager_kernel_address = other.m_ptr_manager_kernel_address;
103  return *this;
104  }
105 
107  stl_allocator &operator=(stl_allocator &&other) noexcept = default;
108 
110  template <typename T2>
112  stl_allocator<T2, manager_kernel_type> &&other) noexcept {
113  m_ptr_manager_kernel_address = other.m_ptr_manager_kernel_address;
114  return *this;
115  }
116 
120  pointer allocate(const size_type n) const { return priv_allocate(n); }
121 
125  void deallocate(pointer ptr, const size_type size) const {
126  return priv_deallocate(ptr, size);
127  }
128 
131  size_type max_size() const noexcept { return priv_max_size(); }
132 
137  template <class... Args>
138  void construct(const pointer &ptr, Args &&...args) const {
139  priv_construct(ptr, std::forward<Args>(args)...);
140  }
141 
144  void destroy(const pointer &ptr) const { priv_destroy(ptr); }
145 
146  // ---------- This class's unique public functions ---------- //
150  return to_raw_pointer(m_ptr_manager_kernel_address);
151  }
152 
153  private:
154  // -------------------- //
155  // Private methods
156  // -------------------- //
157 
158  pointer priv_allocate(const size_type n) const {
159  if (priv_max_size() < n) {
160  throw std::bad_array_new_length();
161  }
162 
164  logger::out(logger::level::error, __FILE__, __LINE__,
165  "nullptr: cannot access to manager kernel");
166  throw std::bad_alloc();
167  }
168  auto* manager_kernel = *get_pointer_to_manager_kernel();
169  if (!manager_kernel) {
170  logger::out(logger::level::error, __FILE__, __LINE__,
171  "nullptr: cannot access to manager kernel");
172  throw std::bad_alloc();
173  }
174 
175  auto addr = pointer(
176  static_cast<value_type *>(manager_kernel->allocate(n * sizeof(T))));
177  if (!addr) {
178  throw std::bad_alloc();
179  }
180 
181  return addr;
182  }
183 
184  void priv_deallocate(pointer ptr,
185  [[maybe_unused]] const size_type size) const noexcept {
187  logger::out(logger::level::error, __FILE__, __LINE__,
188  "nullptr: cannot access to manager kernel");
189  return;
190  }
191  auto manager_kernel = *get_pointer_to_manager_kernel();
192  if (!manager_kernel) {
193  logger::out(logger::level::error, __FILE__, __LINE__,
194  "nullptr: cannot access to manager kernel");
195  return;
196  }
197  manager_kernel->deallocate(to_raw_pointer(ptr));
198  }
199 
200  size_type priv_max_size() const noexcept {
201  return std::numeric_limits<size_type>::max() / sizeof(value_type);
202  }
203 
204  template <class... arg_types>
205  void priv_construct(const pointer &ptr, arg_types &&...args) const {
206  ::new ((void *)to_raw_pointer(ptr))
207  value_type(std::forward<arg_types>(args)...);
208  }
209 
210  void priv_destroy(const pointer &ptr) const {
211  if (!ptr) {
212  logger::out(logger::level::error, __FILE__, __LINE__,
213  "pointer is nullptr");
214  }
215  (*ptr).~value_type();
216  }
217 
218  // -------------------- //
219  // Private fields
220  // -------------------- //
221  private:
222  // (offset)pointer to a raw pointer that points a manager kernel object
223  // allocated in DRAM i.e., offset_ptr<manager_kernel_type *const>
224  typename std::pointer_traits<typename manager_kernel_type::void_pointer>::
225  template rebind<manager_kernel_type *const>
226  m_ptr_manager_kernel_address;
227 };
228 
229 template <typename T, typename kernel>
230 inline bool operator==(const stl_allocator<T, kernel> &rhd,
231  const stl_allocator<T, kernel> &lhd) {
232  // Return true if they point to the same manager kernel
233  return rhd.get_pointer_to_manager_kernel() ==
235 }
236 
237 template <typename T, typename kernel>
238 inline bool operator!=(const stl_allocator<T, kernel> &rhd,
239  const stl_allocator<T, kernel> &lhd) {
240  return !(rhd == lhd);
241 }
242 
243 } // namespace metall
244 
245 #endif // METALL_STL_ALLOCATOR_HPP
static void out(const level lvl, const char *const file_name, const int line_no, const char *const message) noexcept
Log a message.
Definition: logger.hpp:35
@ error
Error logger message.
A STL compatible allocator.
Definition: stl_allocator.hpp:34
typename std::pointer_traits< pointer >::template rebind< const void > const_void_pointer
Definition: stl_allocator.hpp:48
stl_allocator(stl_allocator< T2, manager_kernel_type > allocator_instance) noexcept
Construct a new instance using an instance that has a different T.
Definition: stl_allocator.hpp:81
stl_allocator(manager_kernel_type *const *const pointer_manager_kernel_address) noexcept
Definition: stl_allocator.hpp:75
void deallocate(pointer ptr, const size_type size) const
Deallocates the storage reference by the pointer ptr.
Definition: stl_allocator.hpp:125
void destroy(const pointer &ptr) const
Deconstruct an object of T.
Definition: stl_allocator.hpp:144
~stl_allocator() noexcept=default
Destructor.
pointer allocate(const size_type n) const
Allocates n * sizeof(T) bytes of storage.
Definition: stl_allocator.hpp:120
typename std::pointer_traits< pointer >::difference_type difference_type
Definition: stl_allocator.hpp:50
T value_type
Definition: stl_allocator.hpp:39
typename std::make_unsigned< difference_type >::type size_type
Definition: stl_allocator.hpp:51
stl_allocator & operator=(stl_allocator< T2, manager_kernel_type > &&other) noexcept
Move assign operator for another T.
Definition: stl_allocator.hpp:111
metall_manager_kernel_type manager_kernel_type
Definition: stl_allocator.hpp:52
typename std::pointer_traits< pointer >::template rebind< void > void_pointer
Definition: stl_allocator.hpp:46
stl_allocator(const stl_allocator &other) noexcept=default
Copy constructor.
stl_allocator(stl_allocator &&other) noexcept=default
Move constructor.
typename std::pointer_traits< typename metall_manager_kernel_type::void_pointer >::template rebind< value_type > pointer
Definition: stl_allocator.hpp:42
stl_allocator & operator=(stl_allocator &&other) noexcept=default
Move assign operator.
void construct(const pointer &ptr, Args &&...args) const
Constructs an object of T.
Definition: stl_allocator.hpp:138
typename std::pointer_traits< pointer >::template rebind< const value_type > const_pointer
Definition: stl_allocator.hpp:44
size_type max_size() const noexcept
The size of the theoretical maximum allocation size.
Definition: stl_allocator.hpp:131
manager_kernel_type *const * get_pointer_to_manager_kernel() const
Returns a pointer that points to manager kernel.
Definition: stl_allocator.hpp:149
The top level of namespace of Metall.
Definition: basic_manager.hpp:22
bool operator!=(const stl_allocator< T, kernel > &rhd, const stl_allocator< T, kernel > &lhd)
Definition: stl_allocator.hpp:238
bool operator==(const stl_allocator< T, kernel > &rhd, const stl_allocator< T, kernel > &lhd)
Definition: stl_allocator.hpp:230
Makes another allocator type for type T2.
Definition: stl_allocator.hpp:57