PMDK C++ bindings  1.13.0
This is the C++ bindings documentation for PMDK's libpmemobj.
concurrent_skip_list_impl.hpp
1 // SPDX-License-Identifier: BSD-3-Clause
2 /* Copyright 2020-2021, Intel Corporation */
3 
4 #ifndef PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
5 #define PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
6 
7 #include <algorithm>
8 #include <array>
9 #include <atomic>
10 #include <cstdlib>
11 #include <limits>
12 #include <mutex> /* for std::unique_lock */
13 #include <random>
14 #include <type_traits>
15 
19 #include <libpmemobj++/detail/pair.hpp>
21 #include <libpmemobj++/mutex.hpp>
23 #include <libpmemobj++/pool.hpp>
25 
26 #include <libpmemobj++/experimental/atomic_self_relative_ptr.hpp>
28 
29 /* Windows has a max and a min macros which collides with min() and max()
30  * methods of default_random_generator */
31 #if defined(_WIN32)
32 #if defined(max)
33 #undef max
34 #endif
35 #if defined(min)
36 #undef min
37 #endif
38 #endif
39 
40 namespace pmem
41 {
42 namespace detail
43 {
44 
45 #ifndef NDEBUG
46 inline void
47 try_insert_node_finish_marker()
48 {
49 }
50 #endif
51 
56 template <typename MyAlloc, typename OtherAlloc>
57 inline void
58 allocator_copy_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator,
59  std::true_type)
60 {
61  my_allocator = other_allocator;
62 }
63 
68 template <typename MyAlloc, typename OtherAlloc>
69 inline void
70 allocator_copy_assignment(MyAlloc &, OtherAlloc &, std::false_type)
71 { /* NO COPY */
72 }
73 
78 template <typename MyAlloc, typename OtherAlloc>
79 inline void
80 allocator_move_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator,
81  std::true_type)
82 {
83  my_allocator = std::move(other_allocator);
84 }
85 
90 template <typename MyAlloc, typename OtherAlloc>
91 inline void
92 allocator_move_assignment(MyAlloc &, OtherAlloc &, std::false_type)
93 { /* NO MOVE */
94 }
95 
100 template <typename MyAlloc, typename OtherAlloc>
101 inline void
102 allocator_swap(MyAlloc &my_allocator, OtherAlloc &other_allocator,
103  std::true_type)
104 {
105  std::swap(my_allocator, other_allocator);
106 }
107 
112 template <typename MyAlloc, typename OtherAlloc>
113 inline void
114 allocator_swap(MyAlloc &, OtherAlloc &, std::false_type)
115 { /* NO SWAP */
116 }
117 
118 template <typename Value, typename Mutex = pmem::obj::mutex,
119  typename LockType = std::unique_lock<Mutex>>
120 class skip_list_node {
121 public:
122  using value_type = Value;
123  using size_type = std::size_t;
124  using reference = value_type &;
125  using const_reference = const value_type &;
126  using pointer = value_type *;
127  using const_pointer = const value_type *;
128  using node_pointer =
130  using atomic_node_pointer = std::atomic<node_pointer>;
131  using mutex_type = Mutex;
132  using lock_type = LockType;
133 
134  skip_list_node(size_type levels) : height_(levels)
135  {
136  for (size_type lev = 0; lev < height_; ++lev)
137  detail::create<atomic_node_pointer>(&get_next(lev),
138  nullptr);
139 
140  assert(height() == levels);
141 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
142  /*
143  * Valgrind does not understand atomic semantic and reports
144  * false-postives in drd and helgrind tools.
145  */
146  for (size_type lev = 0; lev < height_; ++lev) {
147  VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
148  sizeof(get_next(lev)));
149  }
150 #endif
151  }
152 
153  skip_list_node(size_type levels, const node_pointer *new_nexts)
154  : height_(levels)
155  {
156  for (size_type lev = 0; lev < height_; ++lev)
157  detail::create<atomic_node_pointer>(&get_next(lev),
158  new_nexts[lev]);
159 
160  assert(height() == levels);
161 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
162  /*
163  * Valgrind does not understand atomic semantic and reports
164  * false-postives in drd and helgrind tools.
165  */
166  for (size_type lev = 0; lev < height_; ++lev) {
167  VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
168  sizeof(get_next(lev)));
169  }
170 #endif
171  }
172 
173  ~skip_list_node()
174  {
175  for (size_type lev = 0; lev < height_; ++lev)
176  detail::destroy<atomic_node_pointer>(get_next(lev));
177  }
178 
179  skip_list_node(const skip_list_node &) = delete;
180 
181  skip_list_node &operator=(const skip_list_node &) = delete;
182 
183  pointer
184  get() noexcept
185  {
186  return &val;
187  }
188 
189  const_pointer
190  get() const noexcept
191  {
192  return &val;
193  }
194 
195  reference
196  value()
197  {
198  return *get();
199  }
200 
201  node_pointer
202  next(size_type level) const
203  {
204  assert(level < height());
205  return get_next(level).load(std::memory_order_acquire);
206  }
207 
212  void
213  set_next_tx(size_type level, node_pointer next)
214  {
215  assert(level < height());
216  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
217  auto &node = get_next(level);
218  obj::flat_transaction::snapshot<atomic_node_pointer>(&node);
219  node.store(next, std::memory_order_release);
220  }
221 
222  void
223  set_next(obj::pool_base pop, size_type level, node_pointer next)
224  {
225  assert(level < height());
226  auto &node = get_next(level);
227  node.store(next, std::memory_order_release);
228  pop.persist(&node, sizeof(node));
229  }
230 
231  void
232  set_nexts(const node_pointer *new_nexts, size_type h)
233  {
234  assert(h == height());
235  auto *nexts = get_nexts();
236 
237  for (size_type i = 0; i < h; i++) {
238  nexts[i].store(new_nexts[i], std::memory_order_relaxed);
239  }
240  }
241 
242  void
243  set_nexts(obj::pool_base pop, const node_pointer *new_nexts,
244  size_type h)
245  {
246  set_nexts(new_nexts, h);
247 
248  auto *nexts = get_nexts();
249  pop.persist(nexts, sizeof(nexts[0]) * h);
250  }
251 
253  size_type
254  height() const
255  {
256  return height_;
257  }
258 
259  lock_type
260  acquire()
261  {
262  return lock_type(mutex);
263  }
264 
265 private:
266  atomic_node_pointer *
267  get_nexts()
268  {
269  return reinterpret_cast<atomic_node_pointer *>(this + 1);
270  }
271 
272  atomic_node_pointer &
273  get_next(size_type level)
274  {
275  auto *arr = get_nexts();
276  return arr[level];
277  }
278 
279  const atomic_node_pointer &
280  get_next(size_type level) const
281  {
282  auto *arr =
283  reinterpret_cast<const atomic_node_pointer *>(this + 1);
284  return arr[level];
285  }
286 
287  mutex_type mutex;
288  union {
289  value_type val;
290  };
291  size_type height_;
292 };
293 
294 template <typename NodeType, bool is_const>
295 class skip_list_iterator {
296  using node_type = NodeType;
297  using node_ptr = typename std::conditional<is_const, const node_type *,
298  node_type *>::type;
299  friend class skip_list_iterator<node_type, true>;
300 
301 public:
302  using value_type = typename node_type::value_type;
303  using iterator_category = std::forward_iterator_tag;
304  using difference_type = std::ptrdiff_t;
305  using reference =
306  typename std::conditional<is_const,
307  typename node_type::const_reference,
308  typename node_type::reference>::type;
309  using pointer = typename std::conditional<is_const, const value_type *,
310  value_type *>::type;
311 
312  skip_list_iterator() : node(nullptr)
313  {
314  }
315 
317  skip_list_iterator(const skip_list_iterator &other) : node(other.node)
318  {
319  }
320 
322  template <typename U = void,
323  typename = typename std::enable_if<is_const, U>::type>
324  skip_list_iterator(const skip_list_iterator<node_type, false> &other)
325  : node(other.node)
326  {
327  }
328 
329  reference operator*() const
330  {
331  return *(node->get());
332  }
333 
334  pointer operator->() const
335  {
336  return node->get();
337  }
338 
339  skip_list_iterator &
340  operator++()
341  {
342  assert(node != nullptr);
343  node = node->next(0).get();
344  return *this;
345  }
346 
347  skip_list_iterator
348  operator++(int)
349  {
350  skip_list_iterator tmp = *this;
351  ++*this;
352  return tmp;
353  }
354 
355  skip_list_iterator &
356  operator=(const skip_list_iterator &other)
357  {
358  node = other.node;
359  return *this;
360  }
361 
362 private:
363  explicit skip_list_iterator(node_type *n) : node(n)
364  {
365  }
366 
367  template <typename T = void,
368  typename = typename std::enable_if<is_const, T>::type>
369  explicit skip_list_iterator(const node_type *n) : node(n)
370  {
371  }
372 
373  node_ptr node;
374 
375  template <typename Traits>
376  friend class concurrent_skip_list;
377 
378  template <typename T, bool M, bool U>
379  friend bool operator==(const skip_list_iterator<T, M> &lhs,
380  const skip_list_iterator<T, U> &rhs);
381 
382  template <typename T, bool M, bool U>
383  friend bool operator!=(const skip_list_iterator<T, M> &lhs,
384  const skip_list_iterator<T, U> &rhs);
385 };
386 
387 template <typename T, bool M, bool U>
388 bool
389 operator==(const skip_list_iterator<T, M> &lhs,
390  const skip_list_iterator<T, U> &rhs)
391 {
392  return lhs.node == rhs.node;
393 }
394 
395 template <typename T, bool M, bool U>
396 bool
397 operator!=(const skip_list_iterator<T, M> &lhs,
398  const skip_list_iterator<T, U> &rhs)
399 {
400  return lhs.node != rhs.node;
401 }
402 
403 struct default_random_generator {
404  using gen_type = std::mt19937_64;
405  using result_type = typename gen_type::result_type;
406 
407  size_t
408  operator()()
409  {
410  static thread_local gen_type engine(
411  static_cast<size_t>(time(0)));
412 
413  return engine();
414  }
415 
416  static constexpr result_type
417  min()
418  {
419  return gen_type::min();
420  }
421 
422  static constexpr result_type
423  max()
424  {
425  return gen_type::max();
426  }
427 };
428 
429 template <typename RndGenerator, size_t MAX_LEVEL>
430 class geometric_level_generator {
431 public:
432  using rnd_generator_type = RndGenerator;
433 
434  static constexpr size_t max_level = MAX_LEVEL;
435 
436  size_t
437  operator()()
438  {
439  /* rnd_generator_type should be thread-safe random number
440  * generator. */
441  static rnd_generator_type gen;
442  /* std::geometric_distribution is not thread-safe. We mark it as
443  * a thread_local to avoid data races. */
444  static thread_local std::geometric_distribution<size_t> d;
445 
446  return (d(gen) % MAX_LEVEL) + 1;
447  }
448 };
449 
478 template <typename Traits>
480 protected:
481  using traits_type = Traits;
482  using key_type = typename traits_type::key_type;
483  using mapped_type = typename traits_type::mapped_type;
484  using value_type = typename traits_type::value_type;
485  using size_type = std::size_t;
486  using difference_type = std::ptrdiff_t;
487  using key_compare = typename traits_type::compare_type;
488  using allocator_type = typename traits_type::allocator_type;
489  using allocator_traits_type = std::allocator_traits<allocator_type>;
490 
491  using reference = value_type &;
492  using const_reference = const value_type &;
493  using pointer = typename allocator_traits_type::pointer;
494  using const_pointer = typename allocator_traits_type::const_pointer;
495 
496  using list_node_type = skip_list_node<value_type>;
497 
498  using iterator = skip_list_iterator<list_node_type, false>;
499  using const_iterator = skip_list_iterator<list_node_type, true>;
500 
501  static constexpr size_type MAX_LEVEL = traits_type::max_level;
502 
503  using random_level_generator_type = geometric_level_generator<
504  typename traits_type::random_generator_type, MAX_LEVEL>;
505  using node_allocator_type = typename std::allocator_traits<
506  allocator_type>::template rebind_alloc<uint8_t>;
507  using node_allocator_traits = typename std::allocator_traits<
508  allocator_type>::template rebind_traits<uint8_t>;
509  using node_ptr = list_node_type *;
510  using const_node_ptr = const list_node_type *;
511  using persistent_node_ptr =
513 
514  using prev_array_type = std::array<node_ptr, MAX_LEVEL>;
515  using next_array_type = std::array<persistent_node_ptr, MAX_LEVEL>;
516  using node_lock_type = typename list_node_type::lock_type;
517  using lock_array = std::array<node_lock_type, MAX_LEVEL>;
518 
519 public:
520  static constexpr bool allow_multimapping =
521  traits_type::allow_multimapping;
522 
532  {
534  init();
535  }
536 
554  const key_compare &comp,
555  const allocator_type &alloc = allocator_type())
556  : _node_allocator(alloc), _compare(comp)
557  {
559  init();
560  }
561 
585  template <class InputIt>
586  concurrent_skip_list(InputIt first, InputIt last,
587  const key_compare &comp = key_compare(),
588  const allocator_type &alloc = allocator_type())
589  : _node_allocator(alloc), _compare(comp)
590  {
592  init();
593  while (first != last)
594  internal_unsafe_emplace(*first++);
595  }
596 
615  : _node_allocator(node_allocator_traits::
616  select_on_container_copy_construction(
617  other._node_allocator)),
618  _compare(other._compare),
619  _rnd_generator(other._rnd_generator)
620  {
622  init();
623  internal_copy(other);
624  assert(_size == other._size);
625  }
626 
647  const allocator_type &alloc)
648  : _node_allocator(alloc),
649  _compare(other._compare),
650  _rnd_generator(other._rnd_generator)
651  {
653  init();
654  internal_copy(other);
655  assert(_size == other._size);
656  }
657 
677  : _node_allocator(std::move(other._node_allocator)),
678  _compare(other._compare),
679  _rnd_generator(other._rnd_generator)
680  {
682  init();
683  internal_move(std::move(other));
684  }
685 
706  const allocator_type &alloc)
707  : _node_allocator(alloc),
708  _compare(other._compare),
709  _rnd_generator(other._rnd_generator)
710  {
712  init();
713  if (alloc == other.get_allocator()) {
714  internal_move(std::move(other));
715  } else {
716  init();
717  internal_copy(std::make_move_iterator(other.begin()),
718  std::make_move_iterator(other.end()));
719  }
720  }
721 
728  void
730  {
731  tls_restore();
732 
733  assert(this->size() ==
734  size_type(std::distance(this->begin(), this->end())));
735  }
736 
749  void
751  {
752  if (dummy_head == nullptr)
753  return;
754 
755  auto pop = get_pool_base();
756  obj::flat_transaction::run(pop, [&] {
757  clear();
758  delete_dummy_head();
759  });
760  }
761 
772  {
773  try {
774  free_data();
775  } catch (...) {
776  std::terminate();
777  }
778  }
779 
797  {
798  if (this == &other)
799  return *this;
800 
802  obj::flat_transaction::run(pop, [&] {
803  using pocca_t = typename node_allocator_traits::
804  propagate_on_container_copy_assignment;
805  clear();
806  allocator_copy_assignment(_node_allocator,
807  other._node_allocator,
808  pocca_t());
809  _compare = other._compare;
810  _rnd_generator = other._rnd_generator;
811 
812  internal_copy(other);
813  });
814  return *this;
815  }
816 
839  {
840  if (this == &other)
841  return *this;
842 
844  obj::flat_transaction::run(pop, [&] {
845  using pocma_t = typename node_allocator_traits::
846  propagate_on_container_move_assignment;
847  clear();
848  if (pocma_t::value ||
849  _node_allocator == other._node_allocator) {
850  delete_dummy_head();
851  allocator_move_assignment(_node_allocator,
852  other._node_allocator,
853  pocma_t());
854  _compare = other._compare;
855  _rnd_generator = other._rnd_generator;
856  internal_move(std::move(other));
857  } else {
858  internal_copy(
859  std::make_move_iterator(other.begin()),
860  std::make_move_iterator(other.end()));
861  }
862  });
863  return *this;
864  }
865 
878  operator=(std::initializer_list<value_type> il)
879  {
881  obj::flat_transaction::run(pop, [&] {
882  clear();
883  for (auto it = il.begin(); it != il.end(); ++it)
885  });
886  return *this;
887  }
888 
905  std::pair<iterator, bool>
906  insert(const value_type &value)
907  {
908  return internal_insert(value.first, value);
909  }
910 
929  template <typename P,
930  typename std::enable_if<
931  std::is_constructible<value_type, P &&>::value>::type>
932  std::pair<iterator, bool>
933  insert(P &&value)
934  {
935  return emplace(std::forward<P>(value));
936  }
937 
954  std::pair<iterator, bool>
955  insert(value_type &&value)
956  {
957  return internal_insert(value.first, std::move(value));
958  }
959 
977  iterator
978  insert(const_iterator hint, const_reference value)
979  {
980  /* Ignore hint */
981  return insert(value).first;
982  }
983 
1004  template <typename P,
1005  typename std::enable_if<
1006  std::is_constructible<value_type, P &&>::value>::type>
1007  iterator
1008  insert(const_iterator hint, P &&value)
1009  {
1010  return emplace_hint(hint, std::forward<P>(value));
1011  }
1012 
1027  template <typename InputIterator>
1028  void
1029  insert(InputIterator first, InputIterator last)
1030  {
1031  for (InputIterator it = first; it != last; ++it)
1032  insert(*it);
1033  }
1034 
1048  void
1049  insert(std::initializer_list<value_type> ilist)
1050  {
1051  insert(ilist.begin(), ilist.end());
1052  }
1053 
1081  template <typename... Args>
1082  std::pair<iterator, bool>
1083  emplace(Args &&... args)
1084  {
1085  return internal_emplace(std::forward<Args>(args)...);
1086  }
1087 
1118  template <typename... Args>
1119  iterator
1120  emplace_hint(const_iterator hint, Args &&... args)
1121  {
1122  /* Ignore hint */
1123  return emplace(std::forward<Args>(args)...).first;
1124  }
1125 
1149  template <typename... Args>
1150  std::pair<iterator, bool>
1151  try_emplace(const key_type &k, Args &&... args)
1152  {
1153  return internal_try_emplace(k, std::forward<Args>(args)...);
1154  }
1155 
1179  template <typename... Args>
1180  std::pair<iterator, bool>
1181  try_emplace(key_type &&k, Args &&... args)
1182  {
1183  return internal_try_emplace(std::move(k),
1184  std::forward<Args>(args)...);
1185  }
1186 
1213  template <typename K, typename... Args>
1214  typename std::enable_if<
1215  has_is_transparent<key_compare>::value &&
1216  std::is_constructible<key_type, K &&>::value,
1217  std::pair<iterator, bool>>::type
1218  try_emplace(K &&k, Args &&... args)
1219  {
1220  return internal_try_emplace(std::forward<K>(k),
1221  std::forward<Args>(args)...);
1222  }
1223 
1240  iterator
1241  unsafe_erase(iterator pos)
1242  {
1243  check_outside_tx();
1244  auto &size_diff = tls_data.local().size_diff;
1245  return internal_erase(pos, size_diff);
1246  }
1247 
1264  iterator
1265  unsafe_erase(const_iterator pos)
1266  {
1267  return unsafe_erase(get_iterator(pos));
1268  }
1269 
1284  iterator
1285  unsafe_erase(const_iterator first, const_iterator last)
1286  {
1287  check_outside_tx();
1288  obj::pool_base pop = get_pool_base();
1289  auto &size_diff = tls_data.local().size_diff;
1290 
1291  obj::flat_transaction::run(pop, [&] {
1292  while (first != last) {
1293  first = internal_erase(first, size_diff);
1294  }
1295  });
1296 
1297  return get_iterator(first);
1298  }
1299 
1312  size_type
1313  unsafe_erase(const key_type &key)
1314  {
1315  std::pair<iterator, iterator> range = equal_range(key);
1316  size_type sz = static_cast<size_type>(
1317  std::distance(range.first, range.second));
1318  unsafe_erase(range.first, range.second);
1319  return sz;
1320  }
1321 
1340  template <
1341  typename K,
1342  typename = typename std::enable_if<
1343  has_is_transparent<key_compare>::value &&
1344  !std::is_convertible<K, iterator>::value &&
1345  !std::is_convertible<K, const_iterator>::value,
1346  K>::type>
1347  size_type
1348  unsafe_erase(const K &key)
1349  {
1350  std::pair<iterator, iterator> range = equal_range(key);
1351  size_type sz = static_cast<size_type>(
1352  std::distance(range.first, range.second));
1353  unsafe_erase(range.first, range.second);
1354  return sz;
1355  }
1356 
1367  iterator
1368  lower_bound(const key_type &key)
1369  {
1370  return internal_get_bound(key, _compare);
1371  }
1372 
1383  const_iterator
1384  lower_bound(const key_type &key) const
1385  {
1386  return internal_get_bound(key, _compare);
1387  }
1388 
1402  template <typename K,
1403  typename = typename std::enable_if<
1404  has_is_transparent<key_compare>::value, K>::type>
1405  iterator
1406  lower_bound(const K &x)
1407  {
1408  return internal_get_bound(x, _compare);
1409  }
1410 
1424  template <typename K,
1425  typename = typename std::enable_if<
1426  has_is_transparent<key_compare>::value, K>::type>
1427  const_iterator
1428  lower_bound(const K &x) const
1429  {
1430  return internal_get_bound(x, _compare);
1431  }
1432 
1443  iterator
1444  find_higher_eq(const key_type &key)
1445  {
1446  return internal_get_bound(key, _compare);
1447  }
1448 
1459  const_iterator
1460  find_higher_eq(const key_type &key) const
1461  {
1462  return internal_get_bound(key, _compare);
1463  }
1464 
1479  template <typename K,
1480  typename = typename std::enable_if<
1481  has_is_transparent<key_compare>::value, K>::type>
1482  iterator
1483  find_higher_eq(const K &x)
1484  {
1485  return internal_get_bound(x, _compare);
1486  }
1487 
1502  template <typename K,
1503  typename = typename std::enable_if<
1504  has_is_transparent<key_compare>::value, K>::type>
1505  const_iterator
1506  find_higher_eq(const K &x) const
1507  {
1508  return internal_get_bound(x, _compare);
1509  }
1510 
1521  iterator
1522  upper_bound(const key_type &key)
1523  {
1524  return internal_get_bound(key, not_greater_compare(_compare));
1525  }
1526 
1537  const_iterator
1538  upper_bound(const key_type &key) const
1539  {
1540  return internal_get_bound(key, not_greater_compare(_compare));
1541  }
1542 
1556  template <typename K,
1557  typename = typename std::enable_if<
1558  has_is_transparent<key_compare>::value, K>::type>
1559  iterator
1560  upper_bound(const K &x)
1561  {
1562  return internal_get_bound(x, not_greater_compare(_compare));
1563  }
1564 
1578  template <typename K,
1579  typename = typename std::enable_if<
1580  has_is_transparent<key_compare>::value, K>::type>
1581  const_iterator
1582  upper_bound(const K &x) const
1583  {
1584  return internal_get_bound(x, not_greater_compare(_compare));
1585  }
1586 
1597  iterator
1598  find_higher(const key_type &key)
1599  {
1600  return internal_get_bound(key, not_greater_compare(_compare));
1601  }
1602 
1613  const_iterator
1614  find_higher(const key_type &key) const
1615  {
1616  return internal_get_bound(key, not_greater_compare(_compare));
1617  }
1618 
1632  template <typename K,
1633  typename = typename std::enable_if<
1634  has_is_transparent<key_compare>::value, K>::type>
1635  iterator
1636  find_higher(const K &x)
1637  {
1638  return internal_get_bound(x, not_greater_compare(_compare));
1639  }
1640 
1654  template <typename K,
1655  typename = typename std::enable_if<
1656  has_is_transparent<key_compare>::value, K>::type>
1657  const_iterator
1658  find_higher(const K &x) const
1659  {
1660  return internal_get_bound(x, not_greater_compare(_compare));
1661  }
1662 
1673  iterator
1674  find_lower(const key_type &key)
1675  {
1676  auto it = internal_get_biggest_less_than(key, _compare);
1677  return iterator(
1678  const_cast<typename iterator::node_ptr>(it.node));
1679  }
1680 
1691  const_iterator
1692  find_lower(const key_type &key) const
1693  {
1694  return internal_get_biggest_less_than(key, _compare);
1695  }
1696 
1710  template <typename K,
1711  typename = typename std::enable_if<
1712  has_is_transparent<key_compare>::value, K>::type>
1713  iterator
1714  find_lower(const K &key)
1715  {
1716  auto it = internal_get_biggest_less_than(key, _compare);
1717  return iterator(
1718  const_cast<typename iterator::node_ptr>(it.node));
1719  }
1720 
1734  template <typename K,
1735  typename = typename std::enable_if<
1736  has_is_transparent<key_compare>::value, K>::type>
1737  const_iterator
1738  find_lower(const K &key) const
1739  {
1740  return internal_get_biggest_less_than(key, _compare);
1741  }
1742 
1753  iterator
1754  find_lower_eq(const key_type &key)
1755  {
1757  key, not_greater_compare(_compare));
1758  return iterator(
1759  const_cast<typename iterator::node_ptr>(it.node));
1760  }
1761 
1772  const_iterator
1773  find_lower_eq(const key_type &key) const
1774  {
1776  key, not_greater_compare(_compare));
1777  }
1778 
1792  template <typename K,
1793  typename = typename std::enable_if<
1794  has_is_transparent<key_compare>::value, K>::type>
1795  iterator
1796  find_lower_eq(const K &key)
1797  {
1799  key, not_greater_compare(_compare));
1800  return iterator(
1801  const_cast<typename iterator::node_ptr>(it.node));
1802  }
1803 
1817  template <typename K,
1818  typename = typename std::enable_if<
1819  has_is_transparent<key_compare>::value, K>::type>
1820  const_iterator
1821  find_lower_eq(const K &key) const
1822  {
1824  key, not_greater_compare(_compare));
1825  }
1826 
1835  iterator
1836  find(const key_type &key)
1837  {
1838  return internal_find(key);
1839  }
1840 
1849  const_iterator
1850  find(const key_type &key) const
1851  {
1852  return internal_find(key);
1853  }
1854 
1867  template <typename K,
1868  typename = typename std::enable_if<
1869  has_is_transparent<key_compare>::value, K>::type>
1870  iterator
1871  find(const K &x)
1872  {
1873  return internal_find(x);
1874  }
1875 
1888  template <typename K,
1889  typename = typename std::enable_if<
1890  has_is_transparent<key_compare>::value, K>::type>
1891  const_iterator
1892  find(const K &x) const
1893  {
1894  return internal_find(x);
1895  }
1896 
1906  size_type
1907  count(const key_type &key) const
1908  {
1909  return internal_count(key);
1910  }
1911 
1924  template <typename K,
1925  typename = typename std::enable_if<
1926  has_is_transparent<key_compare>::value, K>::type>
1927  size_type
1928  count(const K &key) const
1929  {
1930  return internal_count(key);
1931  }
1932 
1941  bool
1942  contains(const key_type &key) const
1943  {
1944  return find(key) != end();
1945  }
1946 
1959  template <typename K,
1960  typename = typename std::enable_if<
1961  has_is_transparent<key_compare>::value, K>::type>
1962  bool
1963  contains(const K &x) const
1964  {
1965  return find(x) != end();
1966  }
1967 
1976  void
1978  {
1979  assert(dummy_head->height() > 0);
1980  obj::pool_base pop = get_pool_base();
1981 
1982  persistent_node_ptr current = dummy_head->next(0);
1983 
1984  obj::flat_transaction::run(pop, [&] {
1985  while (current) {
1986  assert(current->height() > 0);
1987  persistent_node_ptr next = current->next(0);
1988  delete_node(current);
1989  current = next;
1990  }
1991 
1992  node_ptr head = dummy_head.get();
1993  for (size_type i = 0; i < head->height(); ++i) {
1994  head->set_next_tx(i, nullptr);
1995  }
1996 
1997  on_init_size = 0;
1998  tls_data.clear();
1999  obj::flat_transaction::snapshot((size_t *)&_size);
2000  _size = 0;
2001  });
2002  }
2003 
2010  iterator
2012  {
2013  return iterator(dummy_head.get()->next(0).get());
2014  }
2015 
2022  const_iterator
2023  begin() const
2024  {
2025  return const_iterator(dummy_head.get()->next(0).get());
2026  }
2027 
2034  const_iterator
2035  cbegin() const
2036  {
2037  return const_iterator(dummy_head.get()->next(0).get());
2038  }
2039 
2047  iterator
2049  {
2050  return iterator(nullptr);
2051  }
2052 
2060  const_iterator
2061  end() const
2062  {
2063  return const_iterator(nullptr);
2064  }
2065 
2073  const_iterator
2074  cend() const
2075  {
2076  return const_iterator(nullptr);
2077  }
2078 
2085  size_type
2086  size() const
2087  {
2088  return _size.load(std::memory_order_relaxed);
2089  }
2090 
2098  size_type
2099  max_size() const
2100  {
2101  return std::numeric_limits<difference_type>::max();
2102  }
2103 
2110  bool
2111  empty() const
2112  {
2113  return 0 == size();
2114  }
2115 
2121  allocator_type
2123  {
2124  return allocator_type(_node_allocator);
2125  }
2126 
2134  void
2136  {
2137  obj::pool_base pop = get_pool_base();
2138  obj::flat_transaction::run(pop, [&] {
2139  using pocs_t = typename node_allocator_traits::
2140  propagate_on_container_swap;
2141  allocator_swap(_node_allocator, other._node_allocator,
2142  pocs_t());
2143  std::swap(_compare, other._compare);
2144  std::swap(_rnd_generator, other._rnd_generator);
2145  std::swap(dummy_head, other.dummy_head);
2147 
2148  obj::flat_transaction::snapshot((size_t *)&_size);
2150  (size_t *)&(other._size));
2151  _size = other._size.exchange(_size,
2152  std::memory_order_relaxed);
2153  });
2154  }
2155 
2175  std::pair<iterator, iterator>
2176  equal_range(const key_type &key)
2177  {
2178  return std::pair<iterator, iterator>(lower_bound(key),
2179  upper_bound(key));
2180  }
2181 
2201  std::pair<const_iterator, const_iterator>
2202  equal_range(const key_type &key) const
2203  {
2204  return std::pair<const_iterator, const_iterator>(
2205  lower_bound(key), upper_bound(key));
2206  }
2207 
2230  template <typename K,
2231  typename = typename std::enable_if<
2232  has_is_transparent<key_compare>::value, K>::type>
2233  std::pair<iterator, iterator>
2234  equal_range(const K &x)
2235  {
2236  return std::pair<iterator, iterator>(lower_bound(x),
2237  upper_bound(x));
2238  }
2239 
2262  template <typename K,
2263  typename = typename std::enable_if<
2264  has_is_transparent<key_compare>::value, K>::type>
2265  std::pair<const_iterator, const_iterator>
2266  equal_range(const K &key) const
2267  {
2268  return std::pair<const_iterator, const_iterator>(
2269  lower_bound(key), upper_bound(key));
2270  }
2271 
2277  const key_compare &
2278  key_comp() const
2279  {
2280  return _compare;
2281  }
2282 
2288  key_compare &
2290  {
2291  return _compare;
2292  }
2293 
2294 private:
2295  /* Status flags stored in insert_stage field */
2296  enum insert_stage_type : uint8_t { not_started = 0, in_progress = 1 };
2297  /*
2298  * Structure of thread local data.
2299  * Size should be 64 bytes.
2300  */
2301  struct tls_entry_type {
2302  persistent_node_ptr ptr;
2303  obj::p<difference_type> size_diff;
2304  obj::p<insert_stage_type> insert_stage;
2305 
2306  char reserved[64 - sizeof(decltype(ptr)) -
2307  sizeof(decltype(size_diff)) -
2308  sizeof(decltype(insert_stage))];
2309  };
2310  static_assert(sizeof(tls_entry_type) == 64,
2311  "The size of tls_entry_type should be 64 bytes.");
2312 
2320  void
2322  {
2323  if (pmemobj_tx_stage() != TX_STAGE_WORK)
2325  "Function called out of transaction scope.");
2326  }
2327 
2328  /* Helper method which throws an exception when called in a tx */
2329  static inline void
2330  check_outside_tx()
2331  {
2332  if (pmemobj_tx_stage() != TX_STAGE_NONE)
2334  "Function called inside transaction scope.");
2335  }
2336 
2337  void
2338  init()
2339  {
2340  if (pool_uuid == 0)
2341  throw pmem::pool_error("Invalid pool handle.");
2342 
2343  _size = 0;
2344  on_init_size = 0;
2346  }
2347 
2348  void
2349  internal_move(concurrent_skip_list &&other)
2350  {
2351  assert(this->empty());
2352  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2353  dummy_head = other.dummy_head;
2354  other.dummy_head = nullptr;
2355  other.create_dummy_head();
2356 
2357  _size.store(other._size.load(std::memory_order_relaxed),
2358  std::memory_order_relaxed);
2359  on_init_size = other.on_init_size;
2360  }
2361 
2362  static const_reference
2363  get_val(const_node_ptr n)
2364  {
2365  assert(n);
2366  return *(n->get());
2367  }
2368 
2369  static reference
2370  get_val(node_ptr n)
2371  {
2372  assert(n);
2373  return *(n->get());
2374  }
2375 
2376  static const key_type &
2377  get_key(const_node_ptr n)
2378  {
2379  assert(n);
2380  return traits_type::get_key(get_val(n));
2381  }
2382 
2383  template <typename K>
2384  iterator
2385  internal_find(const K &key)
2386  {
2387  iterator it = lower_bound(key);
2388  return (it == end() || _compare(key, traits_type::get_key(*it)))
2389  ? end()
2390  : it;
2391  }
2392 
2393  template <typename K>
2394  const_iterator
2395  internal_find(const K &key) const
2396  {
2397  const_iterator it = lower_bound(key);
2398  return (it == end() || _compare(key, traits_type::get_key(*it)))
2399  ? end()
2400  : it;
2401  }
2402 
2403  template <typename K>
2404  size_type
2405  internal_count(const K &key) const
2406  {
2407  if (allow_multimapping) {
2408  std::pair<const_iterator, const_iterator> range =
2409  equal_range(key);
2410  return static_cast<size_type>(
2411  std::distance(range.first, range.second));
2412  }
2413  return (find(key) == end()) ? size_type(0) : size_type(1);
2414  }
2415 
2426  template <typename K, typename pointer_type, typename comparator>
2427  persistent_node_ptr
2428  internal_find_position(size_type level, pointer_type &prev,
2429  const K &key, const comparator &cmp) const
2430  {
2431  assert(level < prev->height());
2432  persistent_node_ptr next = prev->next(level);
2433  pointer_type curr = next.get();
2434 
2435  while (curr && cmp(get_key(curr), key)) {
2436  prev = curr;
2437  assert(level < prev->height());
2438  next = prev->next(level);
2439  curr = next.get();
2440  }
2441 
2442  return next;
2443  }
2444 
2455  template <typename K>
2456  void
2457  find_insert_pos(prev_array_type &prev_nodes,
2458  next_array_type &next_nodes, const K &key)
2459  {
2460  if (allow_multimapping) {
2461  fill_prev_next_arrays(prev_nodes, next_nodes, key,
2462  not_greater_compare(_compare));
2463  } else {
2464  fill_prev_next_arrays(prev_nodes, next_nodes, key,
2465  _compare);
2466  }
2467  }
2468 
2480  template <typename K, typename comparator>
2481  void
2482  fill_prev_next_arrays(prev_array_type &prev_nodes,
2483  next_array_type &next_nodes, const K &key,
2484  const comparator &cmp)
2485  {
2486  node_ptr prev = dummy_head.get();
2487  prev_nodes.fill(prev);
2488  next_nodes.fill(nullptr);
2489 
2490  for (size_type h = prev->height(); h > 0; --h) {
2491  persistent_node_ptr next =
2492  internal_find_position(h - 1, prev, key, cmp);
2493  prev_nodes[h - 1] = prev;
2494  next_nodes[h - 1] = next;
2495  }
2496  }
2497 
2498  template <typename K, typename... Args>
2499  std::pair<iterator, bool>
2500  internal_try_emplace(K &&key, Args &&... args)
2501  {
2502  return internal_insert(
2503  key, std::piecewise_construct,
2504  std::forward_as_tuple(std::forward<K>(key)),
2505  std::forward_as_tuple(std::forward<Args>(args)...));
2506  }
2507 
2508  template <typename... Args>
2509  std::pair<iterator, bool>
2510  internal_emplace(Args &&... args)
2511  {
2512  check_outside_tx();
2513  tls_entry_type &tls_entry = tls_data.local();
2514  obj::pool_base pop = get_pool_base();
2515 
2516  obj::flat_transaction::run(pop, [&] {
2517  assert(tls_entry.ptr == nullptr);
2518  tls_entry.ptr =
2519  create_node(std::forward<Args>(args)...);
2520  ++tls_entry.size_diff;
2521  tls_entry.insert_stage = not_started;
2522  });
2523 
2524  node_ptr n = tls_entry.ptr.get();
2525  size_type height = n->height();
2526 
2527  std::pair<iterator, bool> insert_result = internal_insert_node(
2528  get_key(n), height,
2529  [&](const next_array_type &next_nodes)
2530  -> persistent_node_ptr & {
2531  assert(tls_entry.insert_stage == not_started);
2532  assert(tls_entry.ptr != nullptr);
2533 
2534  n->set_nexts(pop, next_nodes.data(), height);
2535 
2536  tls_entry.insert_stage = in_progress;
2537  pop.persist(&(tls_entry.insert_stage),
2538  sizeof(tls_entry.insert_stage));
2539 
2540  return tls_entry.ptr;
2541  });
2542 
2543  if (!insert_result.second) {
2544  assert(tls_entry.ptr != nullptr);
2545  assert(tls_entry.insert_stage == not_started);
2546 
2547  obj::flat_transaction::run(pop, [&] {
2548  --tls_entry.size_diff;
2549  delete_node(tls_entry.ptr);
2550  tls_entry.ptr = nullptr;
2551  });
2552  }
2553 
2554  assert(tls_entry.ptr == nullptr);
2555  return insert_result;
2556  }
2557 
2562  template <typename... Args>
2563  std::pair<iterator, bool>
2564  internal_unsafe_emplace(Args &&... args)
2565  {
2567 
2568  persistent_node_ptr new_node =
2569  create_node(std::forward<Args>(args)...);
2570 
2571  node_ptr n = new_node.get();
2572  size_type height = n->height();
2573 
2574  std::pair<iterator, bool> insert_result = internal_insert_node(
2575  get_key(n), height,
2576  [&](const next_array_type &next_nodes)
2577  -> persistent_node_ptr & {
2578  assert(new_node != nullptr);
2579 
2580  n->set_nexts(next_nodes.data(), height);
2581 
2582  return new_node;
2583  });
2584 
2585  if (insert_result.second) {
2586  ++on_init_size;
2587  } else {
2588  assert(new_node != nullptr);
2589 
2590  delete_node(new_node);
2591  }
2592 
2593  return insert_result;
2594  }
2595 
2599  template <typename K, typename... Args>
2600  std::pair<iterator, bool>
2601  internal_insert(const K &key, Args &&... args)
2602  {
2603  check_outside_tx();
2604  tls_entry_type &tls_entry = tls_data.local();
2605  assert(tls_entry.ptr == nullptr);
2606 
2607  size_type height = random_level();
2608 
2609  std::pair<iterator, bool> insert_result = internal_insert_node(
2610  key, height,
2611  [&](const next_array_type &next_nodes)
2612  -> persistent_node_ptr & {
2613  obj::pool_base pop = get_pool_base();
2614 
2616  tls_entry.ptr = create_node(
2617  std::forward_as_tuple(
2618  height, next_nodes.data()),
2619  std::forward_as_tuple(
2620  std::forward<Args>(args)...));
2621 
2622  ++(tls_entry.size_diff);
2623  tls_entry.insert_stage = in_progress;
2625 
2626  assert(tls_entry.ptr != nullptr);
2627  return tls_entry.ptr;
2628  });
2629 
2630  assert(tls_entry.ptr == nullptr);
2631 
2632  return insert_result;
2633  }
2634 
2638  template <typename K, typename PrepareNode>
2639  std::pair<iterator, bool>
2640  internal_insert_node(const K &key, size_type height,
2641  PrepareNode &&prepare_new_node)
2642  {
2643  prev_array_type prev_nodes;
2644  next_array_type next_nodes;
2645  node_ptr n = nullptr;
2646 
2647  do {
2648  find_insert_pos(prev_nodes, next_nodes, key);
2649 
2650  node_ptr next = next_nodes[0].get();
2651  if (next && !allow_multimapping &&
2652  !_compare(key, get_key(next))) {
2653 
2654  return std::pair<iterator, bool>(iterator(next),
2655  false);
2656  }
2657 
2658  } while ((n = try_insert_node(prev_nodes, next_nodes, height,
2659  std::forward<PrepareNode>(
2660  prepare_new_node))) ==
2661  nullptr);
2662 
2663  assert(n);
2664  return std::pair<iterator, bool>(iterator(n), true);
2665  }
2666 
2672  template <typename PrepareNode>
2673  node_ptr
2674  try_insert_node(prev_array_type &prev_nodes,
2675  const next_array_type &next_nodes, size_type height,
2676  PrepareNode &&prepare_new_node)
2677  {
2678  assert(dummy_head->height() >= height);
2679 
2680  lock_array locks;
2681  if (!try_lock_nodes(height, prev_nodes, next_nodes, locks)) {
2682  return nullptr;
2683  }
2684 
2685  node_lock_type new_node_lock;
2686 
2687  persistent_node_ptr &new_node = prepare_new_node(next_nodes);
2688  assert(new_node != nullptr);
2689  node_ptr n = new_node.get();
2690 
2691  /*
2692  * We need to hold lock to the new node until changes
2693  * are committed to persistent domain. Otherwise, the
2694  * new node would be visible to concurrent inserts
2695  * before it is persisted.
2696  */
2697  new_node_lock = n->acquire();
2698 
2699  obj::pool_base pop = get_pool_base();
2700  /*
2701  * In the loop below we are linking a new node to all layers of
2702  * the skip list. Transaction is not required because in case of
2703  * failure the node is reachable via a pointer from persistent
2704  * TLS. During recovery, we will complete the insert. It is also
2705  * OK if concurrent readers will see not a fully-linked node
2706  * because during recovery the insert procedure will be
2707  * completed.
2708  */
2709  for (size_type level = 0; level < height; ++level) {
2710  assert(prev_nodes[level]->height() > level);
2711  assert(prev_nodes[level]->next(level) ==
2712  next_nodes[level]);
2713  assert(prev_nodes[level]->next(level) ==
2714  n->next(level));
2715  prev_nodes[level]->set_next(pop, level, new_node);
2716  }
2717 
2718 #ifndef NDEBUG
2719  try_insert_node_finish_marker();
2720 #endif
2721 
2722  new_node = nullptr;
2723  /* We need to persist the node pointer. Otherwise, on a restart,
2724  * this pointer might be not null but the node can be already
2725  * deleted. */
2726  pop.persist(&new_node, sizeof(new_node));
2727 
2728  ++_size;
2729 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2730  VALGRIND_PMC_DO_FLUSH(&_size, sizeof(_size));
2731 #endif
2732 
2733  assert(n);
2734  return n;
2735  }
2736 
2741  bool
2742  check_prev_array(const prev_array_type &prevs, size_type height)
2743  {
2744  for (size_type l = 1; l < height; ++l) {
2745  if (prevs[l] == dummy_head.get()) {
2746  continue;
2747  }
2748 
2749  assert(prevs[l - 1] != dummy_head.get());
2750  assert(!_compare(get_key(prevs[l - 1]),
2751  get_key(prevs[l])));
2752  }
2753 
2754  return true;
2755  }
2756 
2757  bool
2758  try_lock_nodes(size_type height, prev_array_type &prevs,
2759  const next_array_type &nexts, lock_array &locks)
2760  {
2761  assert(check_prev_array(prevs, height));
2762 
2763  for (size_type l = 0; l < height; ++l) {
2764  if (l == 0 || prevs[l] != prevs[l - 1]) {
2765  locks[l] = prevs[l]->acquire();
2766  }
2767 
2768  persistent_node_ptr next = prevs[l]->next(l);
2769  if (next != nexts[l])
2770  /* Other thread inserted to this position and
2771  * modified the pointer before we acquired the
2772  * lock */
2773  return false;
2774  }
2775 
2776  return true;
2777  }
2778 
2790  template <typename K, typename comparator>
2791  const_iterator
2792  internal_get_bound(const K &key, const comparator &cmp) const
2793  {
2794  const_node_ptr prev = dummy_head.get();
2795  assert(prev->height() > 0);
2796  persistent_node_ptr next = nullptr;
2797 
2798  for (size_type h = prev->height(); h > 0; --h) {
2799  next = internal_find_position(h - 1, prev, key, cmp);
2800  }
2801 
2802  return const_iterator(next.get());
2803  }
2804 
2816  template <typename K, typename comparator>
2817  iterator
2818  internal_get_bound(const K &key, const comparator &cmp)
2819  {
2820  node_ptr prev = dummy_head.get();
2821  assert(prev->height() > 0);
2822  persistent_node_ptr next = nullptr;
2823 
2824  for (size_type h = prev->height(); h > 0; --h) {
2825  next = internal_find_position(h - 1, prev, key, cmp);
2826  }
2827 
2828  return iterator(next.get());
2829  }
2830 
2842  template <typename K, typename comparator>
2843  const_iterator
2845  const comparator &cmp) const
2846  {
2847  const_node_ptr prev = dummy_head.get();
2848  assert(prev->height() > 0);
2849 
2850  for (size_type h = prev->height(); h > 0; --h) {
2851  internal_find_position(h - 1, prev, key, cmp);
2852  }
2853 
2854  if (prev == dummy_head.get())
2855  return end();
2856 
2857  return const_iterator(prev);
2858  }
2859 
2860  iterator
2861  internal_erase(const_iterator pos, obj::p<difference_type> &size_diff)
2862  {
2863  assert(pos != end());
2864 
2865  obj::pool_base pop = get_pool_base();
2866 
2867  std::pair<persistent_node_ptr, persistent_node_ptr>
2868  extract_result(nullptr, nullptr);
2869 
2870  obj::flat_transaction::run(pop, [&] {
2871  extract_result = internal_extract(pos);
2872 
2873  /* Make sure that node was extracted */
2874  assert(extract_result.first != nullptr);
2875  delete_node(extract_result.first);
2876  --size_diff;
2877  obj::flat_transaction::snapshot((size_type *)&_size);
2878  --_size;
2879  });
2880 
2881  return iterator(extract_result.second.get());
2882  }
2883 
2887  std::pair<persistent_node_ptr, persistent_node_ptr>
2888  internal_extract(const_iterator it)
2889  {
2890  assert(dummy_head->height() > 0);
2891  assert(it != end());
2892  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2893 
2894  const key_type &key = traits_type::get_key(*it);
2895 
2896  prev_array_type prev_nodes;
2897  next_array_type next_nodes;
2898 
2899  fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
2900 
2901  node_ptr erase_node = next_nodes[0].get();
2902  assert(erase_node != nullptr);
2903 
2904  if (!_compare(key, get_key(erase_node))) {
2905  /* XXX: this assertion will fail in case of multimap
2906  * because we take the first node with the same key.
2907  * Need to extend algorithm for mutimap. */
2908  assert(erase_node == it.node);
2909  return internal_extract_node(prev_nodes, next_nodes,
2910  erase_node);
2911  }
2912 
2913  return std::pair<persistent_node_ptr, persistent_node_ptr>(
2914  nullptr, nullptr);
2915  }
2916 
2917  std::pair<persistent_node_ptr, persistent_node_ptr>
2918  internal_extract_node(const prev_array_type &prev_nodes,
2919  const next_array_type &next_nodes,
2920  node_ptr erase_node)
2921  {
2922  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2923  assert(erase_node != nullptr);
2924  for (size_type level = 0; level < erase_node->height();
2925  ++level) {
2926  assert(prev_nodes[level]->height() > level);
2927  assert(next_nodes[level].get() == erase_node);
2928  prev_nodes[level]->set_next_tx(level,
2929  erase_node->next(level));
2930  }
2931 
2932  return std::pair<persistent_node_ptr, persistent_node_ptr>(
2933  next_nodes[0], erase_node->next(0));
2934  }
2935 
2940  obj::pool_base
2942  {
2943  PMEMobjpool *pop = pmemobj_pool_by_ptr(this);
2944  return obj::pool_base(pop);
2945  }
2946 
2947  void
2948  internal_copy(const concurrent_skip_list &other)
2949  {
2950  internal_copy(other.begin(), other.end());
2951  }
2952 
2953  template <typename Iterator>
2954  void
2955  internal_copy(Iterator first, Iterator last)
2956  {
2957  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2958 
2959  prev_array_type prev_nodes;
2960  prev_nodes.fill(dummy_head.get());
2961  size_type sz = 0;
2962 
2963  for (; first != last; ++first, ++sz) {
2964  persistent_node_ptr new_node = create_node(*first);
2965  node_ptr n = new_node.get();
2966  for (size_type level = 0; level < n->height();
2967  ++level) {
2968  prev_nodes[level]->set_next_tx(level, new_node);
2969  prev_nodes[level] = n;
2970  }
2971  }
2972 
2973  on_init_size = sz;
2974  /*
2975  * As internal_swap can only be called from one thread, and
2976  * there can be an outer transaction we must make sure that size
2977  * change is transactional
2978  */
2979  obj::flat_transaction::snapshot((size_type *)&_size);
2980  _size = sz;
2981  assert(std::is_sorted(
2982  this->begin(), this->end(),
2983  [&](const value_type &lhs, const value_type &rhs) {
2984  return lhs.first < rhs.first;
2985  }));
2986  }
2987 
2989  size_type
2991  {
2992  return _rnd_generator();
2993  }
2994 
2995  static size_type
2996  calc_node_size(size_type height)
2997  {
2998  return sizeof(list_node_type) +
2999  height * sizeof(typename list_node_type::node_pointer);
3000  }
3001 
3003  template <typename... Args>
3004  persistent_node_ptr
3005  create_node(Args &&... args)
3006  {
3007  size_type levels = random_level();
3008 
3009  return create_node(
3010  std::forward_as_tuple(levels),
3011  std::forward_as_tuple(std::forward<Args>(args)...));
3012  }
3013 
3014  template <typename... NodeArgs, typename... ValueArgs>
3015  persistent_node_ptr
3016  create_node(std::tuple<NodeArgs...> &&node_args,
3017  std::tuple<ValueArgs...> &&value_args)
3018  {
3019  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3020 
3021  persistent_node_ptr node = creates_dummy_node(
3022  std::forward<std::tuple<NodeArgs...>>(node_args),
3023  index_sequence_for<NodeArgs...>{});
3024 
3025  construct_value_type(
3026  node,
3027  std::forward<std::tuple<ValueArgs...>>(value_args),
3028  index_sequence_for<ValueArgs...>{});
3029 
3030  return node;
3031  }
3032 
3033  template <typename Tuple, size_t... I>
3034  void
3035  construct_value_type(persistent_node_ptr node, Tuple &&args,
3036  index_sequence<I...>)
3037  {
3038  node_ptr new_node = node.get();
3039 
3040  node_allocator_traits::construct(
3041  _node_allocator, new_node->get(),
3042  std::get<I>(std::forward<Tuple>(args))...);
3043  }
3044 
3050  void
3052  {
3053  dummy_head = creates_dummy_node(MAX_LEVEL);
3054  }
3055 
3056  template <typename Tuple, size_t... I>
3057  persistent_node_ptr
3058  creates_dummy_node(Tuple &&args, index_sequence<I...>)
3059  {
3060  return creates_dummy_node(
3061  std::get<I>(std::forward<Tuple>(args))...);
3062  }
3063 
3073  template <typename... Args>
3074  persistent_node_ptr
3075  creates_dummy_node(size_type height, Args &&... args)
3076  {
3077  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3078  size_type sz = calc_node_size(height);
3079 
3081  node_allocator_traits::allocate(_node_allocator, sz)
3082  .raw();
3083 
3084  assert(n != nullptr);
3085 
3086  node_allocator_traits::construct(_node_allocator, n.get(),
3087  height,
3088  std::forward<Args>(args)...);
3089 
3090  return n;
3091  }
3092 
3093  template <bool is_dummy = false>
3094  void
3095  delete_node(persistent_node_ptr &node)
3096  {
3097  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3098  node_ptr n = node.get();
3099  size_type sz = calc_node_size(n->height());
3100 
3101  /* Destroy value */
3102  if (!is_dummy)
3103  node_allocator_traits::destroy(_node_allocator,
3104  n->get());
3105  /* Destroy node */
3106  node_allocator_traits::destroy(_node_allocator, n);
3107  /* Deallocate memory */
3108  deallocate_node(node, sz);
3109  node = nullptr;
3110  }
3111 
3112  void
3113  deallocate_node(persistent_node_ptr &node, size_type sz)
3114  {
3115  /*
3116  * Each node object has different size which depends on number
3117  * of layers the node is linked. Therefore, allocate/deallocate
3118  * just a raw byte array. persistent_ptr<uint8_t> is used as a
3119  * pointer to raw array of bytes.
3120  */
3122  node.to_persistent_ptr().raw();
3123  node_allocator_traits::deallocate(_node_allocator, tmp, sz);
3124  }
3125 
3126  void
3127  delete_dummy_head()
3128  {
3129  assert(dummy_head != nullptr);
3130  delete_node<true>(dummy_head);
3131  assert(dummy_head == nullptr);
3132  }
3133 
3134  iterator
3135  get_iterator(const_iterator it)
3136  {
3137  return iterator(
3138  const_cast<typename iterator::node_ptr>(it.node));
3139  }
3140 
3142  void
3144  {
3145  int64_t last_run_size = 0;
3146  obj::pool_base pop = get_pool_base();
3147 
3148  for (auto &tls_entry : tls_data) {
3149  persistent_node_ptr &node = tls_entry.ptr;
3150  auto &size_diff = tls_entry.size_diff;
3151  if (node) {
3152  /*
3153  * We are completing inserts which were in
3154  * progress before the crash because readers
3155  * might saw incompleted inserts before the
3156  * crash. We set the in_progress flag inside
3157  * try_insert_node function when we locked the
3158  * predecessors for the new node, therefore,
3159  * only single node with the same key might have
3160  * the in_progress status.
3161  */
3162  if (tls_entry.insert_stage == in_progress) {
3163  complete_insert(tls_entry);
3164  } else {
3165  obj::flat_transaction::run(pop, [&] {
3166  --(tls_entry.size_diff);
3167  delete_node(node);
3168  node = nullptr;
3169  });
3170  }
3171  }
3172 
3173  assert(node == nullptr);
3174 
3175  last_run_size += size_diff;
3176  }
3177 
3178  /* Make sure that on_init_size + last_run_size >= 0 */
3179  assert(last_run_size >= 0 ||
3180  on_init_size >
3181  static_cast<size_type>(std::abs(last_run_size)));
3182  obj::flat_transaction::run(pop, [&] {
3183  tls_data.clear();
3184  on_init_size += static_cast<size_t>(last_run_size);
3185  });
3186  _size = on_init_size;
3187 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
3188  VALGRIND_PMC_DO_FLUSH(&_size, sizeof(_size));
3189 #endif
3190  }
3191 
3192  void
3193  complete_insert(tls_entry_type &tls_entry)
3194  {
3195  persistent_node_ptr &node = tls_entry.ptr;
3196  assert(node != nullptr);
3197  assert(tls_entry.insert_stage == in_progress);
3198  prev_array_type prev_nodes;
3199  next_array_type next_nodes;
3200  node_ptr n = node.get();
3201  const key_type &key = get_key(n);
3202  size_type height = n->height();
3203 
3204  fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
3205  obj::pool_base pop = get_pool_base();
3206 
3207  /* Node was partially linked */
3208  for (size_type level = 0; level < height; ++level) {
3209  assert(prev_nodes[level]->height() > level);
3210  assert(prev_nodes[level]->next(level) ==
3211  next_nodes[level]);
3212 
3213  if (prev_nodes[level]->next(level) != node) {
3214  /* Otherwise, node already linked on
3215  * this layer */
3216  assert(n->next(level) == next_nodes[level]);
3217  prev_nodes[level]->set_next(pop, level, node);
3218  }
3219  }
3220 
3221  node = nullptr;
3222  pop.persist(&node, sizeof(node));
3223  }
3224 
3225  struct not_greater_compare {
3226  const key_compare &my_less_compare;
3227 
3228  not_greater_compare(const key_compare &less_compare)
3229  : my_less_compare(less_compare)
3230  {
3231  }
3232 
3233  template <typename K1, typename K2>
3234  bool
3235  operator()(const K1 &first, const K2 &second) const
3236  {
3237  return !my_less_compare(second, first);
3238  }
3239  };
3240 
3241  const uint64_t pool_uuid = pmemobj_oid(this).pool_uuid_lo;
3242  node_allocator_type _node_allocator;
3243  key_compare _compare;
3244  random_level_generator_type _rnd_generator;
3245  persistent_node_ptr dummy_head;
3246 
3247  enumerable_thread_specific<tls_entry_type> tls_data;
3248 
3249  std::atomic<size_type> _size;
3250 
3257 }; /* class concurrent_skip_list */
3258 
3259 template <typename Key, typename Value, typename KeyCompare,
3260  typename RND_GENERATOR, typename Allocator, bool AllowMultimapping,
3261  size_t MAX_LEVEL>
3262 class map_traits {
3263 public:
3264  static constexpr size_t max_level = MAX_LEVEL;
3265  using random_generator_type = RND_GENERATOR;
3266  using key_type = Key;
3267  using mapped_type = Value;
3268  using compare_type = KeyCompare;
3269  using value_type = pair<const key_type, mapped_type>;
3270  using reference = value_type &;
3271  using const_reference = const value_type &;
3272  using allocator_type = Allocator;
3273 
3280  constexpr static bool allow_multimapping = AllowMultimapping;
3281 
3282  static const key_type &
3283  get_key(const_reference val)
3284  {
3285  return val.first;
3286  }
3287 }; /* class map_traits */
3288 
3289 } /* namespace detail */
3290 } /* namespace pmem */
3291 
3292 #endif /* PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP */
void check_tx_stage_work() const
Private helper function.
Definition: concurrent_skip_list_impl.hpp:2321
std::pair< iterator, bool > internal_insert(const K &key, Args &&...args)
Construct and insert new node to the skip list in a thread-safe way.
Definition: concurrent_skip_list_impl.hpp:2601
std::pair< iterator, bool > try_emplace(const key_type &k, Args &&...args)
If a key equivalent to k already exists in the container, does nothing.
Definition: concurrent_skip_list_impl.hpp:1151
reference local()
Returns data reference for the current thread.
Definition: enumerable_thread_specific.hpp:305
Persistent self-relative smart pointer.
std::pair< const_iterator, const_iterator > equal_range(const K &key) const
Returns a range containing all elements with the given key in the container.
Definition: concurrent_skip_list_impl.hpp:2266
std::pair< iterator, bool > insert(value_type &&value)
Inserts value using move semantic.
Definition: concurrent_skip_list_impl.hpp:955
void free_data()
Should be called before concurrent_skip_list destructor is called.
Definition: concurrent_skip_list_impl.hpp:750
concurrent_skip_list(const concurrent_skip_list &other, const allocator_type &alloc)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:646
Persistent pointer class.
Definition: common.hpp:130
iterator find_higher(const key_type &key)
Returns an iterator pointing to the first element that is greater than key.
Definition: concurrent_skip_list_impl.hpp:1598
void swap(pmem::obj::array< T, N > &lhs, pmem::obj::array< T, N > &rhs)
Non-member swap function.
Definition: array.hpp:909
const_iterator internal_get_biggest_less_than(const K &key, const comparator &cmp) const
Returns an iterator pointing to the last element from the list for which cmp(element, key) is true.
Definition: concurrent_skip_list_impl.hpp:2844
const_iterator upper_bound(const K &x) const
Returns an iterator pointing to the first element that compares greater to the value x...
Definition: concurrent_skip_list_impl.hpp:1582
Pmem-resident mutex.
const_iterator find_lower_eq(const key_type &key) const
Returns a const iterator pointing to the biggest element that is less than or equal to key...
Definition: concurrent_skip_list_impl.hpp:1773
node_ptr try_insert_node(prev_array_type &prev_nodes, const next_array_type &next_nodes, size_type height, PrepareNode &&prepare_new_node)
Try to insert new node to the skip list.
Definition: concurrent_skip_list_impl.hpp:2674
The non-template pool base class.
Definition: pool.hpp:50
iterator unsafe_erase(const_iterator first, const_iterator last)
Removes the elements in the range [first; last), which must be a valid range in *this.
Definition: concurrent_skip_list_impl.hpp:1285
typename detail::transaction_base< true >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:762
const_iterator find_higher_eq(const K &x) const
Returns an iterator pointing to the first element that compares not less (i.e.
Definition: concurrent_skip_list_impl.hpp:1506
concurrent_skip_list(concurrent_skip_list &&other, const allocator_type &alloc)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:705
allocator_type get_allocator() const
Returns the allocator associated with the container.
Definition: concurrent_skip_list_impl.hpp:2122
void clear()
Removes all elements from the container.
Definition: enumerable_thread_specific.hpp:345
bool empty() const
Checks if the container has no elements, i.e.
Definition: concurrent_skip_list_impl.hpp:2111
Custom pool error class.
Definition: pexceptions.hpp:45
const_iterator cend() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2074
void clear()
Erases all elements from the container transactionally.
Definition: concurrent_skip_list_impl.hpp:1977
size_type count(const key_type &key) const
Returns the number of elements with key that compares equivalent to the specified argument...
Definition: concurrent_skip_list_impl.hpp:1907
void allocator_copy_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, std::true_type)
Copy assignment implementation for allocator if propagate_on_container_copy_assignment == true_type...
Definition: concurrent_skip_list_impl.hpp:58
iterator find(const K &x)
Finds an element with key that compares equivalent to the value x.
Definition: concurrent_skip_list_impl.hpp:1871
void swap(concurrent_skip_list &other)
Exchanges the contents of the container with those of other transactionally.
Definition: concurrent_skip_list_impl.hpp:2135
obj::pool_base get_pool_base() const
Get the persistent memory pool where hashmap resides.
Definition: concurrent_skip_list_impl.hpp:2941
Definition: concurrent_hash_map.hpp:42
void runtime_initialize()
Initialize concurrent_skip_list after process restart.
Definition: concurrent_skip_list_impl.hpp:729
void fill_prev_next_arrays(prev_array_type &prev_nodes, next_array_type &next_nodes, const K &key, const comparator &cmp)
The method finds successor and predecessor nodes on each level of the skip list for the given...
Definition: concurrent_skip_list_impl.hpp:2482
concurrent_skip_list()
Default constructor.
Definition: concurrent_skip_list_impl.hpp:531
std::pair< iterator, bool > try_emplace(key_type &&k, Args &&...args)
If a key equivalent to k already exists in the container, does nothing.
Definition: concurrent_skip_list_impl.hpp:1181
bool contains(const key_type &key) const
Checks if there is an element with key equivalent to key in the container.
Definition: concurrent_skip_list_impl.hpp:1942
size_type random_level()
Generate random level.
Definition: concurrent_skip_list_impl.hpp:2990
const_iterator end() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2061
const_iterator find(const K &x) const
Finds an element with key that compares equivalent to the value x.
Definition: concurrent_skip_list_impl.hpp:1892
size_type size() const
Returns the number of elements in the container, i.e.
Definition: concurrent_skip_list_impl.hpp:2086
const_iterator find(const key_type &key) const
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1850
const_iterator find_higher_eq(const key_type &key) const
Returns an iterator pointing to the first element that is not less than (i.e.
Definition: concurrent_skip_list_impl.hpp:1460
const_iterator find_lower(const K &key) const
Returns a const iterator pointing to the biggest element that is less than key.
Definition: concurrent_skip_list_impl.hpp:1738
C++ pmemobj transactions.
iterator unsafe_erase(iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1241
bool check_prev_array(const prev_array_type &prevs, size_type height)
Used only inside asserts.
Definition: concurrent_skip_list_impl.hpp:2742
iterator lower_bound(const key_type &key)
Returns an iterator pointing to the first element that is not less than (i.e.
Definition: concurrent_skip_list_impl.hpp:1368
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
const_iterator internal_get_bound(const K &key, const comparator &cmp) const
Returns an iterator pointing to the first element from the list for which cmp(element, key) is false.
Definition: concurrent_skip_list_impl.hpp:2792
std::pair< iterator, bool > internal_insert_node(const K &key, size_type height, PrepareNode &&prepare_new_node)
Try to insert new node to the skip list in a thread-safe way.
Definition: concurrent_skip_list_impl.hpp:2640
element_type * get() const noexcept
Get the direct pointer.
Definition: self_relative_ptr.hpp:196
Functions for destroying arrays.
iterator find_lower_eq(const K &key)
Returns an iterator pointing to the biggest element that is less than or equal to key...
Definition: concurrent_skip_list_impl.hpp:1796
iterator find_higher(const K &x)
Returns an iterator pointing to the first element that compares greater to the value x...
Definition: concurrent_skip_list_impl.hpp:1636
Commonly used functionality.
iterator unsafe_erase(const_iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1265
const_iterator begin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2023
std::pair< persistent_node_ptr, persistent_node_ptr > internal_extract(const_iterator it)
Definition: concurrent_skip_list_impl.hpp:2888
std::pair< iterator, iterator > equal_range(const K &x)
Returns a range containing all elements with the given key in the container.
Definition: concurrent_skip_list_impl.hpp:2234
iterator begin()
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2011
const_iterator lower_bound(const K &x) const
Returns an iterator pointing to the first element that compares not less (i.e.
Definition: concurrent_skip_list_impl.hpp:1428
const_iterator find_higher(const key_type &key) const
Returns an iterator pointing to the first element that is greater than key.
Definition: concurrent_skip_list_impl.hpp:1614
key_compare & key_comp()
Returns a reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2289
Persistent memory aware implementation of the concurrent skip list.
Definition: concurrent_skip_list_impl.hpp:479
iterator insert(const_iterator hint, P &&value)
Inserts value in the position as close as possible, just prior to hint.
Definition: concurrent_skip_list_impl.hpp:1008
iterator find(const key_type &key)
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1836
void insert(std::initializer_list< value_type > ilist)
Inserts elements from initializer list ilist.
Definition: concurrent_skip_list_impl.hpp:1049
concurrent_skip_list & operator=(concurrent_skip_list &&other)
Move assignment operator.
Definition: concurrent_skip_list_impl.hpp:838
obj::p< size_type > on_init_size
This variable holds real size after the skip list is initialized.
Definition: concurrent_skip_list_impl.hpp:3256
const_iterator find_lower_eq(const K &key) const
Returns a const iterator pointing to the biggest element that is less than or equal to key...
Definition: concurrent_skip_list_impl.hpp:1821
iterator find_higher_eq(const K &x)
Returns an iterator pointing to the first element that compares not less (i.e.
Definition: concurrent_skip_list_impl.hpp:1483
size_type unsafe_erase(const K &key)
Removes the element (if one exists) with the key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1348
const PMEMoid & raw() const noexcept
Get PMEMoid encapsulated by this object.
Definition: persistent_ptr_base.hpp:151
void persist(const void *addr, size_t len) noexcept
Performs persist operation on a given chunk of memory.
Definition: pool.hpp:279
Persistent memory resident mutex implementation.
Definition: mutex.hpp:31
const_iterator lower_bound(const key_type &key) const
Returns an iterator pointing to the first element that is not less than (i.e.
Definition: concurrent_skip_list_impl.hpp:1384
const_iterator find_higher(const K &x) const
Returns an iterator pointing to the first element that compares greater to the value x...
Definition: concurrent_skip_list_impl.hpp:1658
concurrent_skip_list & operator=(const concurrent_skip_list &other)
Copy assignment operator.
Definition: concurrent_skip_list_impl.hpp:796
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:325
A persistent version of thread-local storage.
size_type count(const K &key) const
Returns the number of elements with key that compares equivalent to the specified argument...
Definition: concurrent_skip_list_impl.hpp:1928
void find_insert_pos(prev_array_type &prev_nodes, next_array_type &next_nodes, const K &key)
The method finds insert position for the given.
Definition: concurrent_skip_list_impl.hpp:2457
void swap(p &other)
Swaps two p objects of the same type.
Definition: p.hpp:140
iterator insert(const_iterator hint, const_reference value)
Inserts value in the position as close as possible, just prior to hint.
Definition: concurrent_skip_list_impl.hpp:978
void create_dummy_head()
Creates dummy head.
Definition: concurrent_skip_list_impl.hpp:3051
const key_compare & key_comp() const
Returns a const reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2278
std::pair< iterator, iterator > equal_range(const key_type &key)
Returns a range containing all elements with the given key in the container.
Definition: concurrent_skip_list_impl.hpp:2176
iterator end()
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2048
const_iterator upper_bound(const key_type &key) const
Returns an iterator pointing to the first element that is greater than key.
Definition: concurrent_skip_list_impl.hpp:1538
std::enable_if< has_is_transparent< key_compare >::value &&std::is_constructible< key_type, K && >::value, std::pair< iterator, bool > >::type try_emplace(K &&k, Args &&...args)
If a key equivalent to k already exists in the container, does nothing.
Definition: concurrent_skip_list_impl.hpp:1218
iterator find_higher_eq(const key_type &key)
Returns an iterator pointing to the first element that is not less than (i.e.
Definition: concurrent_skip_list_impl.hpp:1444
iterator upper_bound(const K &x)
Returns an iterator pointing to the first element that compares greater to the value x...
Definition: concurrent_skip_list_impl.hpp:1560
std::pair< iterator, bool > insert(P &&value)
Inserts value.
Definition: concurrent_skip_list_impl.hpp:933
iterator find_lower(const key_type &key)
Returns an iterator pointing to the biggest element that is less than key.
Definition: concurrent_skip_list_impl.hpp:1674
void allocator_move_assignment(MyAlloc &my_allocator, OtherAlloc &other_allocator, std::true_type)
Move assignment implementation for allocator if propagate_on_container_move_assignment == true_type...
Definition: concurrent_skip_list_impl.hpp:80
Commonly used SFINAE helpers.
iterator upper_bound(const key_type &key)
Returns an iterator pointing to the first element that is greater than key.
Definition: concurrent_skip_list_impl.hpp:1522
Persistent smart pointer.
concurrent_skip_list(const key_compare &comp, const allocator_type &alloc=allocator_type())
Constructs an empty container.
Definition: concurrent_skip_list_impl.hpp:553
iterator emplace_hint(const_iterator hint, Args &&...args)
Inserts a new element to the container as close as possible to the position just before hint...
Definition: concurrent_skip_list_impl.hpp:1120
Persistent self-relative pointer class.
Definition: self_relative_ptr.hpp:25
iterator find_lower(const K &key)
Returns an iterator pointing to the biggest element that is less than key.
Definition: concurrent_skip_list_impl.hpp:1714
~concurrent_skip_list()
Destructor.
Definition: concurrent_skip_list_impl.hpp:771
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &...locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:823
size_type max_size() const
Returns the maximum number of elements the container is able to hold due to system or library impleme...
Definition: concurrent_skip_list_impl.hpp:2099
iterator internal_get_bound(const K &key, const comparator &cmp)
Returns an iterator pointing to the first element from the list for which cmp(element, key) is false.
Definition: concurrent_skip_list_impl.hpp:2818
concurrent_skip_list(const concurrent_skip_list &other)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:614
persistent_node_ptr creates_dummy_node(size_type height, Args &&...args)
Creates new node, value_type should be constructed separately.
Definition: concurrent_skip_list_impl.hpp:3075
void insert(InputIterator first, InputIterator last)
Inserts elements from range [first, last).
Definition: concurrent_skip_list_impl.hpp:1029
const_iterator find_lower(const key_type &key) const
Returns a const iterator pointing to the biggest element that is less than key.
Definition: concurrent_skip_list_impl.hpp:1692
void allocator_swap(MyAlloc &my_allocator, OtherAlloc &other_allocator, std::true_type)
Swap implementation for allocators if propagate_on_container_swap == true_type.
Definition: concurrent_skip_list_impl.hpp:102
p< T > & operator++(p< T > &pp)
Prefix increment operator overload.
Definition: pext.hpp:48
persistent_node_ptr internal_find_position(size_type level, pointer_type &prev, const K &key, const comparator &cmp) const
Finds position on the.
Definition: concurrent_skip_list_impl.hpp:2428
Custom transaction error class.
Definition: pexceptions.hpp:176
Persistent memory namespace.
Definition: allocation_flag.hpp:14
iterator lower_bound(const K &x)
Returns an iterator pointing to the first element that compares not less (i.e.
Definition: concurrent_skip_list_impl.hpp:1406
size_type unsafe_erase(const key_type &key)
Removes the element (if one exists) with the key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1313
concurrent_skip_list(InputIt first, InputIt last, const key_compare &comp=key_compare(), const allocator_type &alloc=allocator_type())
Constructs the container with the contents of the range [first, last).
Definition: concurrent_skip_list_impl.hpp:586
concurrent_skip_list & operator=(std::initializer_list< value_type > il)
Replaces the contents with those identified by initializer list il.
Definition: concurrent_skip_list_impl.hpp:878
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 ...
Definition: transaction.hpp:428
persistent_node_ptr create_node(Args &&...args)
Creates new node.
Definition: concurrent_skip_list_impl.hpp:3005
void tls_restore()
Process any information which was saved to tls and clears tls.
Definition: concurrent_skip_list_impl.hpp:3143
std::pair< iterator, bool > emplace(Args &&...args)
Inserts a new element into the container constructed in-place with the given args if there is no elem...
Definition: concurrent_skip_list_impl.hpp:1083
iterator find_lower_eq(const key_type &key)
Returns an iterator pointing to the biggest element that is less than or equal to key...
Definition: concurrent_skip_list_impl.hpp:1754
bool contains(const K &x) const
Checks if there is an element with key that compares equivalent to the value x.
Definition: concurrent_skip_list_impl.hpp:1963
std::pair< iterator, bool > insert(const value_type &value)
Inserts value in a thread-safe way.
Definition: concurrent_skip_list_impl.hpp:906
std::pair< iterator, bool > internal_unsafe_emplace(Args &&...args)
Not thread-safe but can be called within a transaction.
Definition: concurrent_skip_list_impl.hpp:2564
concurrent_skip_list(concurrent_skip_list &&other)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:676
const_iterator cbegin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2035
C++ pmemobj pool.
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
std::pair< const_iterator, const_iterator > equal_range(const key_type &key) const
Returns a range containing all elements with the given key in the container.
Definition: concurrent_skip_list_impl.hpp:2202