PMDK C++ bindings  1.13.0-git23.gf49772ac
This is the C++ bindings documentation for PMDK's libpmemobj.
tagged_ptr.hpp
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2021, Intel Corporation */
3 
4 #ifndef LIBPMEMOBJ_CPP_TAGGED_PTR
5 #define LIBPMEMOBJ_CPP_TAGGED_PTR
6 
7 #include <cassert>
8 
10 #include <libpmemobj++/experimental/atomic_self_relative_ptr.hpp>
13 
14 namespace pmem
15 {
16 namespace detail
17 {
18 
19 template <typename P1, typename P2, typename PointerType>
20 struct tagged_ptr_impl {
21  tagged_ptr_impl() = default;
22  tagged_ptr_impl(const tagged_ptr_impl &rhs) = default;
23 
24  tagged_ptr_impl(std::nullptr_t) : ptr(nullptr)
25  {
26  assert(!(bool)*this);
27  }
28 
29  tagged_ptr_impl(const PointerType &ptr) : ptr(ptr)
30  {
31  }
32 
33  tagged_ptr_impl(const obj::persistent_ptr<P1> &ptr)
34  : ptr(add_tag(ptr.get()))
35  {
36  assert(get<P1>() == ptr.get());
37  }
38 
39  tagged_ptr_impl(const obj::persistent_ptr<P2> &ptr) : ptr(ptr.get())
40  {
41  assert(get<P2>() == ptr.get());
42  }
43 
44  tagged_ptr_impl &operator=(const tagged_ptr_impl &rhs) = default;
45 
46  tagged_ptr_impl &operator=(std::nullptr_t)
47  {
48  ptr = nullptr;
49  assert(!(bool)*this);
50 
51  return *this;
52  }
53  tagged_ptr_impl &
54  operator=(const obj::persistent_ptr<P1> &rhs)
55  {
56  ptr = add_tag(rhs.get());
57  assert(get<P1>() == rhs.get());
58 
59  return *this;
60  }
61  tagged_ptr_impl &
62  operator=(const obj::persistent_ptr<P2> &rhs)
63  {
64  ptr = rhs.get();
65  assert(get<P2>() == rhs.get());
66 
67  return *this;
68  }
69 
70  bool
71  operator==(const tagged_ptr_impl &rhs) const
72  {
73  return ptr.to_byte_pointer() == rhs.ptr.to_byte_pointer();
74  }
75  bool
76  operator!=(const tagged_ptr_impl &rhs) const
77  {
78  return !(*this == rhs);
79  }
80 
81  bool
82  operator==(const P1 *rhs) const
83  {
84  return is_tagged() && get<P1>() == rhs;
85  }
86 
87  bool
88  operator!=(const P2 *rhs) const
89  {
90  return !(*this == rhs);
91  }
92 
93  void
94  swap(tagged_ptr_impl &rhs)
95  {
96  ptr.swap(rhs.ptr);
97  }
98 
99  template <typename T>
100  typename std::enable_if<std::is_same<T, P1>::value, bool>::type
101  is() const
102  {
103  return is_tagged();
104  }
105 
106  template <typename T>
107  typename std::enable_if<!std::is_same<T, P1>::value, bool>::type
108  is() const
109  {
110  return !is_tagged();
111  }
112 
113  template <typename T>
114  typename std::enable_if<std::is_same<T, P1>::value, T *>::type
115  get() const
116  {
117  assert(is_tagged());
118  return static_cast<P1 *>(remove_tag(ptr.to_void_pointer()));
119  }
120 
121  template <typename T>
122  typename std::enable_if<!std::is_same<T, P1>::value, T *>::type
123  get() const
124  {
125  assert(!is_tagged());
126  return static_cast<P2 *>(ptr.to_void_pointer());
127  }
128 
129  P2 *operator->() const
130  {
131  return get<P2>();
132  }
133 
134  explicit operator bool() const noexcept
135  {
136  return remove_tag(ptr.to_void_pointer()) != nullptr;
137  }
138 
139 private:
140  static constexpr uintptr_t IS_TAGGED = 1;
141  void *
142  add_tag(P1 *ptr) const
143  {
144  auto tagged =
145  reinterpret_cast<uintptr_t>(ptr) | uintptr_t(IS_TAGGED);
146  return reinterpret_cast<P1 *>(tagged);
147  }
148 
149  void *
150  remove_tag(void *ptr) const
151  {
152  auto untagged = reinterpret_cast<uintptr_t>(ptr) &
153  ~uintptr_t(IS_TAGGED);
154  return reinterpret_cast<void *>(untagged);
155  }
156 
157  bool
158  is_tagged() const
159  {
160  auto value = reinterpret_cast<uintptr_t>(ptr.to_void_pointer());
161  return value & uintptr_t(IS_TAGGED);
162  }
163 
164  PointerType ptr;
165 
166 #ifndef DOXYGEN_SHOULD_SKIP_THIS
167  friend std::atomic<tagged_ptr_impl<
168  P1, P2, obj::experimental::self_relative_ptr<void>>>;
169 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
170 };
171 
172 template <typename P1, typename P2>
173 using tagged_ptr =
174  tagged_ptr_impl<P1, P2, obj::experimental::self_relative_ptr<void>>;
175 
176 } /* namespace detail */
177 } /* namespace pmem */
178 
179 namespace std
180 {
181 
182 template <typename P1, typename P2>
183 struct atomic<pmem::detail::tagged_ptr<P1, P2>> {
184 private:
185  using ptr_type = pmem::detail::tagged_ptr_impl<
186  P1, P2,
187  atomic<pmem::obj::experimental::self_relative_ptr<void>>>;
188  using value_type = pmem::detail::tagged_ptr<P1, P2>;
189 
190 public:
191  /*
192  * Constructors
193  */
194  constexpr atomic() noexcept = default;
195 
196  atomic(value_type value) : ptr()
197  {
198  store(value);
199  }
200 
201  atomic(const atomic &) = delete;
202 
203  void
204  store(value_type desired,
205  std::memory_order order = std::memory_order_seq_cst) noexcept
206  {
207  LIBPMEMOBJ_CPP_ANNOTATE_HAPPENS_BEFORE(order, &ptr.ptr);
208  ptr.ptr.store(desired.ptr, order);
209  }
210 
211  void
212  store_with_snapshot(value_type desired,
213  std::memory_order order = std::memory_order_seq_cst)
214  {
215  LIBPMEMOBJ_CPP_ANNOTATE_HAPPENS_BEFORE(order, &ptr.ptr);
217  ptr.ptr.store(desired.ptr, order);
218  }
219 
220  void
221  store_with_snapshot_release(value_type desired)
222  {
223  store_with_snapshot(desired, std::memory_order_release);
224  }
225 
226  value_type
227  load(std::memory_order order = std::memory_order_seq_cst) const noexcept
228  {
229 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
230  VALGRIND_HG_DISABLE_CHECKING(&ptr.ptr, sizeof(ptr.ptr));
231 #endif
232  auto ret = this->ptr.ptr.load(order);
233  LIBPMEMOBJ_CPP_ANNOTATE_HAPPENS_AFTER(order, &ptr.ptr);
234  return value_type(ret);
235  }
236 
237  value_type
238  load_acquire() const noexcept
239  {
240  return load(std::memory_order_acquire);
241  }
242 
243  void
244  swap(atomic<pmem::detail::tagged_ptr<P1, P2>> &rhs)
245  {
246  auto tmp = rhs.load();
247  rhs.store_with_snapshot(this->load());
248  this->store_with_snapshot(tmp);
249  }
250 
251  atomic<pmem::detail::tagged_ptr<P1, P2>> &
252  operator=(atomic<pmem::detail::tagged_ptr<P1, P2>> &) = delete;
253 
254 private:
255  ptr_type ptr;
256 };
257 
258 } /* namespace std */
259 
260 #endif /* LIBPMEMOBJ_CPP_TAGGED_PTR */
static void snapshot(const T *addr, size_t num=1)
Takes a “snapshot” of given elements of type T number (1 by default), located at the given address pt...
Definition: transaction.hpp:428
Commonly used functionality.
bool operator!=(const allocator< T, P, Tr > &lhs, const OtherAllocator &rhs)
Determines if memory from another allocator can be deallocated from this one.
Definition: allocator.hpp:536
T & get(pmem::obj::array< T, N > &a)
Non-member get function.
Definition: array.hpp:919
bool operator==(standard_alloc_policy< T > const &, standard_alloc_policy< T2 > const &)
Determines if memory from another allocator can be deallocated from this one.
Definition: allocator.hpp:420
void swap(pmem::obj::array< T, N > &lhs, pmem::obj::array< T, N > &rhs)
Non-member swap function.
Definition: array.hpp:909
Persistent memory namespace.
Definition: allocation_flag.hpp:15
Persistent smart pointer.
Persistent self-relative smart pointer.