PMDK C++ bindings  1.11.1
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, 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::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::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::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::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::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::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::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::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 
2128  void
2130  {
2131  obj::pool_base pop = get_pool_base();
2132  obj::transaction::run(pop, [&] {
2133  using pocs_t = typename node_allocator_traits::
2134  propagate_on_container_swap;
2135  allocator_swap(_node_allocator, other._node_allocator,
2136  pocs_t());
2137  std::swap(_compare, other._compare);
2138  std::swap(_rnd_generator, other._rnd_generator);
2139  std::swap(dummy_head, other.dummy_head);
2141 
2142  obj::transaction::snapshot((size_t *)&_size);
2143  obj::transaction::snapshot((size_t *)&(other._size));
2144  _size = other._size.exchange(_size,
2145  std::memory_order_relaxed);
2146  });
2147  }
2148 
2168  std::pair<iterator, iterator>
2169  equal_range(const key_type &key)
2170  {
2171  return std::pair<iterator, iterator>(lower_bound(key),
2172  upper_bound(key));
2173  }
2174 
2194  std::pair<const_iterator, const_iterator>
2195  equal_range(const key_type &key) const
2196  {
2197  return std::pair<const_iterator, const_iterator>(
2198  lower_bound(key), upper_bound(key));
2199  }
2200 
2223  template <typename K,
2224  typename = typename std::enable_if<
2225  has_is_transparent<key_compare>::value, K>::type>
2226  std::pair<iterator, iterator>
2227  equal_range(const K &x)
2228  {
2229  return std::pair<iterator, iterator>(lower_bound(x),
2230  upper_bound(x));
2231  }
2232 
2255  template <typename K,
2256  typename = typename std::enable_if<
2257  has_is_transparent<key_compare>::value, K>::type>
2258  std::pair<const_iterator, const_iterator>
2259  equal_range(const K &key) const
2260  {
2261  return std::pair<const_iterator, const_iterator>(
2262  lower_bound(key), upper_bound(key));
2263  }
2264 
2270  const key_compare &
2271  key_comp() const
2272  {
2273  return _compare;
2274  }
2275 
2281  key_compare &
2283  {
2284  return _compare;
2285  }
2286 
2287 private:
2288  /* Status flags stored in insert_stage field */
2289  enum insert_stage_type : uint8_t { not_started = 0, in_progress = 1 };
2290  /*
2291  * Structure of thread local data.
2292  * Size should be 64 bytes.
2293  */
2294  struct tls_entry_type {
2295  persistent_node_ptr ptr;
2296  obj::p<difference_type> size_diff;
2297  obj::p<insert_stage_type> insert_stage;
2298 
2299  char reserved[64 - sizeof(decltype(ptr)) -
2300  sizeof(decltype(size_diff)) -
2301  sizeof(decltype(insert_stage))];
2302  };
2303  static_assert(sizeof(tls_entry_type) == 64,
2304  "The size of tls_entry_type should be 64 bytes.");
2305 
2313  void
2315  {
2316  if (pmemobj_tx_stage() != TX_STAGE_WORK)
2318  "Function called out of transaction scope.");
2319  }
2320 
2321  /* Helper method which throws an exception when called in a tx */
2322  static inline void
2323  check_outside_tx()
2324  {
2325  if (pmemobj_tx_stage() != TX_STAGE_NONE)
2327  "Function called inside transaction scope.");
2328  }
2329 
2330  void
2331  init()
2332  {
2333  if (pool_uuid == 0)
2334  throw pmem::pool_error("Invalid pool handle.");
2335 
2336  _size = 0;
2337  on_init_size = 0;
2339  }
2340 
2341  void
2342  internal_move(concurrent_skip_list &&other)
2343  {
2344  assert(this->empty());
2345  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2346  dummy_head = other.dummy_head;
2347  other.dummy_head = nullptr;
2348  other.create_dummy_head();
2349 
2350  _size.store(other._size.load(std::memory_order_relaxed),
2351  std::memory_order_relaxed);
2352  on_init_size = other.on_init_size;
2353  }
2354 
2355  static const_reference
2356  get_val(const_node_ptr n)
2357  {
2358  assert(n);
2359  return *(n->get());
2360  }
2361 
2362  static reference
2363  get_val(node_ptr n)
2364  {
2365  assert(n);
2366  return *(n->get());
2367  }
2368 
2369  static const key_type &
2370  get_key(const_node_ptr n)
2371  {
2372  assert(n);
2373  return traits_type::get_key(get_val(n));
2374  }
2375 
2376  template <typename K>
2377  iterator
2378  internal_find(const K &key)
2379  {
2380  iterator it = lower_bound(key);
2381  return (it == end() || _compare(key, traits_type::get_key(*it)))
2382  ? end()
2383  : it;
2384  }
2385 
2386  template <typename K>
2387  const_iterator
2388  internal_find(const K &key) const
2389  {
2390  const_iterator it = lower_bound(key);
2391  return (it == end() || _compare(key, traits_type::get_key(*it)))
2392  ? end()
2393  : it;
2394  }
2395 
2396  template <typename K>
2397  size_type
2398  internal_count(const K &key) const
2399  {
2400  if (allow_multimapping) {
2401  std::pair<const_iterator, const_iterator> range =
2402  equal_range(key);
2403  return static_cast<size_type>(
2404  std::distance(range.first, range.second));
2405  }
2406  return (find(key) == end()) ? size_type(0) : size_type(1);
2407  }
2408 
2419  template <typename K, typename pointer_type, typename comparator>
2420  persistent_node_ptr
2421  internal_find_position(size_type level, pointer_type &prev,
2422  const K &key, const comparator &cmp) const
2423  {
2424  assert(level < prev->height());
2425  persistent_node_ptr next = prev->next(level);
2426  pointer_type curr = next.get();
2427 
2428  while (curr && cmp(get_key(curr), key)) {
2429  prev = curr;
2430  assert(level < prev->height());
2431  next = prev->next(level);
2432  curr = next.get();
2433  }
2434 
2435  return next;
2436  }
2437 
2448  template <typename K>
2449  void
2450  find_insert_pos(prev_array_type &prev_nodes,
2451  next_array_type &next_nodes, const K &key)
2452  {
2453  if (allow_multimapping) {
2454  fill_prev_next_arrays(prev_nodes, next_nodes, key,
2455  not_greater_compare(_compare));
2456  } else {
2457  fill_prev_next_arrays(prev_nodes, next_nodes, key,
2458  _compare);
2459  }
2460  }
2461 
2473  template <typename K, typename comparator>
2474  void
2475  fill_prev_next_arrays(prev_array_type &prev_nodes,
2476  next_array_type &next_nodes, const K &key,
2477  const comparator &cmp)
2478  {
2479  node_ptr prev = dummy_head.get();
2480  prev_nodes.fill(prev);
2481  next_nodes.fill(nullptr);
2482 
2483  for (size_type h = prev->height(); h > 0; --h) {
2484  persistent_node_ptr next =
2485  internal_find_position(h - 1, prev, key, cmp);
2486  prev_nodes[h - 1] = prev;
2487  next_nodes[h - 1] = next;
2488  }
2489  }
2490 
2491  template <typename K, typename... Args>
2492  std::pair<iterator, bool>
2493  internal_try_emplace(K &&key, Args &&... args)
2494  {
2495  return internal_insert(
2496  key, std::piecewise_construct,
2497  std::forward_as_tuple(std::forward<K>(key)),
2498  std::forward_as_tuple(std::forward<Args>(args)...));
2499  }
2500 
2501  template <typename... Args>
2502  std::pair<iterator, bool>
2503  internal_emplace(Args &&... args)
2504  {
2505  check_outside_tx();
2506  tls_entry_type &tls_entry = tls_data.local();
2507  obj::pool_base pop = get_pool_base();
2508 
2509  obj::transaction::run(pop, [&] {
2510  assert(tls_entry.ptr == nullptr);
2511  tls_entry.ptr =
2512  create_node(std::forward<Args>(args)...);
2513  ++tls_entry.size_diff;
2514  tls_entry.insert_stage = not_started;
2515  });
2516 
2517  node_ptr n = tls_entry.ptr.get();
2518  size_type height = n->height();
2519 
2520  std::pair<iterator, bool> insert_result = internal_insert_node(
2521  get_key(n), height,
2522  [&](const next_array_type &next_nodes)
2523  -> persistent_node_ptr & {
2524  assert(tls_entry.insert_stage == not_started);
2525  assert(tls_entry.ptr != nullptr);
2526 
2527  n->set_nexts(pop, next_nodes.data(), height);
2528 
2529  tls_entry.insert_stage = in_progress;
2530  pop.persist(&(tls_entry.insert_stage),
2531  sizeof(tls_entry.insert_stage));
2532 
2533  return tls_entry.ptr;
2534  });
2535 
2536  if (!insert_result.second) {
2537  assert(tls_entry.ptr != nullptr);
2538  assert(tls_entry.insert_stage == not_started);
2539 
2540  obj::transaction::run(pop, [&] {
2541  --tls_entry.size_diff;
2542  delete_node(tls_entry.ptr);
2543  tls_entry.ptr = nullptr;
2544  });
2545  }
2546 
2547  assert(tls_entry.ptr == nullptr);
2548  return insert_result;
2549  }
2550 
2555  template <typename... Args>
2556  std::pair<iterator, bool>
2557  internal_unsafe_emplace(Args &&... args)
2558  {
2560 
2561  persistent_node_ptr new_node =
2562  create_node(std::forward<Args>(args)...);
2563 
2564  node_ptr n = new_node.get();
2565  size_type height = n->height();
2566 
2567  std::pair<iterator, bool> insert_result = internal_insert_node(
2568  get_key(n), height,
2569  [&](const next_array_type &next_nodes)
2570  -> persistent_node_ptr & {
2571  assert(new_node != nullptr);
2572 
2573  n->set_nexts(next_nodes.data(), height);
2574 
2575  return new_node;
2576  });
2577 
2578  if (insert_result.second) {
2579  ++on_init_size;
2580  } else {
2581  assert(new_node != nullptr);
2582 
2583  delete_node(new_node);
2584  }
2585 
2586  return insert_result;
2587  }
2588 
2592  template <typename K, typename... Args>
2593  std::pair<iterator, bool>
2594  internal_insert(const K &key, Args &&... args)
2595  {
2596  check_outside_tx();
2597  tls_entry_type &tls_entry = tls_data.local();
2598  assert(tls_entry.ptr == nullptr);
2599 
2600  size_type height = random_level();
2601 
2602  std::pair<iterator, bool> insert_result = internal_insert_node(
2603  key, height,
2604  [&](const next_array_type &next_nodes)
2605  -> persistent_node_ptr & {
2606  obj::pool_base pop = get_pool_base();
2607 
2608  obj::transaction::manual tx(pop);
2609  tls_entry.ptr = create_node(
2610  std::forward_as_tuple(
2611  height, next_nodes.data()),
2612  std::forward_as_tuple(
2613  std::forward<Args>(args)...));
2614 
2615  ++(tls_entry.size_diff);
2616  tls_entry.insert_stage = in_progress;
2618 
2619  assert(tls_entry.ptr != nullptr);
2620  return tls_entry.ptr;
2621  });
2622 
2623  assert(tls_entry.ptr == nullptr);
2624 
2625  return insert_result;
2626  }
2627 
2631  template <typename K, typename PrepareNode>
2632  std::pair<iterator, bool>
2633  internal_insert_node(const K &key, size_type height,
2634  PrepareNode &&prepare_new_node)
2635  {
2636  prev_array_type prev_nodes;
2637  next_array_type next_nodes;
2638  node_ptr n = nullptr;
2639 
2640  do {
2641  find_insert_pos(prev_nodes, next_nodes, key);
2642 
2643  node_ptr next = next_nodes[0].get();
2644  if (next && !allow_multimapping &&
2645  !_compare(key, get_key(next))) {
2646 
2647  return std::pair<iterator, bool>(iterator(next),
2648  false);
2649  }
2650 
2651  } while ((n = try_insert_node(prev_nodes, next_nodes, height,
2652  std::forward<PrepareNode>(
2653  prepare_new_node))) ==
2654  nullptr);
2655 
2656  assert(n);
2657  return std::pair<iterator, bool>(iterator(n), true);
2658  }
2659 
2665  template <typename PrepareNode>
2666  node_ptr
2667  try_insert_node(prev_array_type &prev_nodes,
2668  const next_array_type &next_nodes, size_type height,
2669  PrepareNode &&prepare_new_node)
2670  {
2671  assert(dummy_head->height() >= height);
2672 
2673  lock_array locks;
2674  if (!try_lock_nodes(height, prev_nodes, next_nodes, locks)) {
2675  return nullptr;
2676  }
2677 
2678  node_lock_type new_node_lock;
2679 
2680  persistent_node_ptr &new_node = prepare_new_node(next_nodes);
2681  assert(new_node != nullptr);
2682  node_ptr n = new_node.get();
2683 
2684  /*
2685  * We need to hold lock to the new node until changes
2686  * are committed to persistent domain. Otherwise, the
2687  * new node would be visible to concurrent inserts
2688  * before it is persisted.
2689  */
2690  new_node_lock = n->acquire();
2691 
2692  obj::pool_base pop = get_pool_base();
2693  /*
2694  * In the loop below we are linking a new node to all layers of
2695  * the skip list. Transaction is not required because in case of
2696  * failure the node is reachable via a pointer from persistent
2697  * TLS. During recovery, we will complete the insert. It is also
2698  * OK if concurrent readers will see not a fully-linked node
2699  * because during recovery the insert procedure will be
2700  * completed.
2701  */
2702  for (size_type level = 0; level < height; ++level) {
2703  assert(prev_nodes[level]->height() > level);
2704  assert(prev_nodes[level]->next(level) ==
2705  next_nodes[level]);
2706  assert(prev_nodes[level]->next(level) ==
2707  n->next(level));
2708  prev_nodes[level]->set_next(pop, level, new_node);
2709  }
2710 
2711 #ifndef NDEBUG
2712  try_insert_node_finish_marker();
2713 #endif
2714 
2715  new_node = nullptr;
2716  /* We need to persist the node pointer. Otherwise, on a restart,
2717  * this pointer might be not null but the node can be already
2718  * deleted. */
2719  pop.persist(&new_node, sizeof(new_node));
2720 
2721  ++_size;
2722 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2723  VALGRIND_PMC_DO_FLUSH(&_size, sizeof(_size));
2724 #endif
2725 
2726  assert(n);
2727  return n;
2728  }
2729 
2734  bool
2735  check_prev_array(const prev_array_type &prevs, size_type height)
2736  {
2737  for (size_type l = 1; l < height; ++l) {
2738  if (prevs[l] == dummy_head.get()) {
2739  continue;
2740  }
2741 
2742  assert(prevs[l - 1] != dummy_head.get());
2743  assert(!_compare(get_key(prevs[l - 1]),
2744  get_key(prevs[l])));
2745  }
2746 
2747  return true;
2748  }
2749 
2750  bool
2751  try_lock_nodes(size_type height, prev_array_type &prevs,
2752  const next_array_type &nexts, lock_array &locks)
2753  {
2754  assert(check_prev_array(prevs, height));
2755 
2756  for (size_type l = 0; l < height; ++l) {
2757  if (l == 0 || prevs[l] != prevs[l - 1]) {
2758  locks[l] = prevs[l]->acquire();
2759  }
2760 
2761  persistent_node_ptr next = prevs[l]->next(l);
2762  if (next != nexts[l])
2763  /* Other thread inserted to this position and
2764  * modified the pointer before we acquired the
2765  * lock */
2766  return false;
2767  }
2768 
2769  return true;
2770  }
2771 
2783  template <typename K, typename comparator>
2784  const_iterator
2785  internal_get_bound(const K &key, const comparator &cmp) const
2786  {
2787  const_node_ptr prev = dummy_head.get();
2788  assert(prev->height() > 0);
2789  persistent_node_ptr next = nullptr;
2790 
2791  for (size_type h = prev->height(); h > 0; --h) {
2792  next = internal_find_position(h - 1, prev, key, cmp);
2793  }
2794 
2795  return const_iterator(next.get());
2796  }
2797 
2809  template <typename K, typename comparator>
2810  iterator
2811  internal_get_bound(const K &key, const comparator &cmp)
2812  {
2813  node_ptr prev = dummy_head.get();
2814  assert(prev->height() > 0);
2815  persistent_node_ptr next = nullptr;
2816 
2817  for (size_type h = prev->height(); h > 0; --h) {
2818  next = internal_find_position(h - 1, prev, key, cmp);
2819  }
2820 
2821  return iterator(next.get());
2822  }
2823 
2835  template <typename K, typename comparator>
2836  const_iterator
2838  const comparator &cmp) const
2839  {
2840  const_node_ptr prev = dummy_head.get();
2841  assert(prev->height() > 0);
2842 
2843  for (size_type h = prev->height(); h > 0; --h) {
2844  internal_find_position(h - 1, prev, key, cmp);
2845  }
2846 
2847  if (prev == dummy_head.get())
2848  return end();
2849 
2850  return const_iterator(prev);
2851  }
2852 
2853  iterator
2854  internal_erase(const_iterator pos, obj::p<difference_type> &size_diff)
2855  {
2856  assert(pos != end());
2857 
2858  obj::pool_base pop = get_pool_base();
2859 
2860  std::pair<persistent_node_ptr, persistent_node_ptr>
2861  extract_result(nullptr, nullptr);
2862 
2863  obj::transaction::run(pop, [&] {
2864  extract_result = internal_extract(pos);
2865 
2866  /* Make sure that node was extracted */
2867  assert(extract_result.first != nullptr);
2868  delete_node(extract_result.first);
2869  --size_diff;
2870  obj::transaction::snapshot((size_type *)&_size);
2871  --_size;
2872  });
2873 
2874  return iterator(extract_result.second.get());
2875  }
2876 
2880  std::pair<persistent_node_ptr, persistent_node_ptr>
2881  internal_extract(const_iterator it)
2882  {
2883  assert(dummy_head->height() > 0);
2884  assert(it != end());
2885  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2886 
2887  const key_type &key = traits_type::get_key(*it);
2888 
2889  prev_array_type prev_nodes;
2890  next_array_type next_nodes;
2891 
2892  fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
2893 
2894  node_ptr erase_node = next_nodes[0].get();
2895  assert(erase_node != nullptr);
2896 
2897  if (!_compare(key, get_key(erase_node))) {
2898  /* XXX: this assertion will fail in case of multimap
2899  * because we take the first node with the same key.
2900  * Need to extend algorithm for mutimap. */
2901  assert(erase_node == it.node);
2902  return internal_extract_node(prev_nodes, next_nodes,
2903  erase_node);
2904  }
2905 
2906  return std::pair<persistent_node_ptr, persistent_node_ptr>(
2907  nullptr, nullptr);
2908  }
2909 
2910  std::pair<persistent_node_ptr, persistent_node_ptr>
2911  internal_extract_node(const prev_array_type &prev_nodes,
2912  const next_array_type &next_nodes,
2913  node_ptr erase_node)
2914  {
2915  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2916  assert(erase_node != nullptr);
2917  for (size_type level = 0; level < erase_node->height();
2918  ++level) {
2919  assert(prev_nodes[level]->height() > level);
2920  assert(next_nodes[level].get() == erase_node);
2921  prev_nodes[level]->set_next_tx(level,
2922  erase_node->next(level));
2923  }
2924 
2925  return std::pair<persistent_node_ptr, persistent_node_ptr>(
2926  next_nodes[0], erase_node->next(0));
2927  }
2928 
2933  obj::pool_base
2935  {
2936  PMEMobjpool *pop = pmemobj_pool_by_ptr(this);
2937  return obj::pool_base(pop);
2938  }
2939 
2940  void
2941  internal_copy(const concurrent_skip_list &other)
2942  {
2943  internal_copy(other.begin(), other.end());
2944  }
2945 
2946  template <typename Iterator>
2947  void
2948  internal_copy(Iterator first, Iterator last)
2949  {
2950  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2951 
2952  prev_array_type prev_nodes;
2953  prev_nodes.fill(dummy_head.get());
2954  size_type sz = 0;
2955 
2956  for (; first != last; ++first, ++sz) {
2957  persistent_node_ptr new_node = create_node(*first);
2958  node_ptr n = new_node.get();
2959  for (size_type level = 0; level < n->height();
2960  ++level) {
2961  prev_nodes[level]->set_next_tx(level, new_node);
2962  prev_nodes[level] = n;
2963  }
2964  }
2965 
2966  on_init_size = sz;
2967  /*
2968  * As internal_swap can only be called from one thread, and
2969  * there can be an outer transaction we must make sure that size
2970  * change is transactional
2971  */
2972  obj::transaction::snapshot((size_type *)&_size);
2973  _size = sz;
2974  assert(std::is_sorted(
2975  this->begin(), this->end(),
2976  [&](const value_type &lhs, const value_type &rhs) {
2977  return lhs.first < rhs.first;
2978  }));
2979  }
2980 
2982  size_type
2984  {
2985  return _rnd_generator();
2986  }
2987 
2988  static size_type
2989  calc_node_size(size_type height)
2990  {
2991  return sizeof(list_node_type) +
2992  height * sizeof(typename list_node_type::node_pointer);
2993  }
2994 
2996  template <typename... Args>
2997  persistent_node_ptr
2998  create_node(Args &&... args)
2999  {
3000  size_type levels = random_level();
3001 
3002  return create_node(
3003  std::forward_as_tuple(levels),
3004  std::forward_as_tuple(std::forward<Args>(args)...));
3005  }
3006 
3007  template <typename... NodeArgs, typename... ValueArgs>
3008  persistent_node_ptr
3009  create_node(std::tuple<NodeArgs...> &&node_args,
3010  std::tuple<ValueArgs...> &&value_args)
3011  {
3012  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3013 
3014  persistent_node_ptr node = creates_dummy_node(
3015  std::forward<std::tuple<NodeArgs...>>(node_args),
3016  index_sequence_for<NodeArgs...>{});
3017 
3018  construct_value_type(
3019  node,
3020  std::forward<std::tuple<ValueArgs...>>(value_args),
3021  index_sequence_for<ValueArgs...>{});
3022 
3023  return node;
3024  }
3025 
3026  template <typename Tuple, size_t... I>
3027  void
3028  construct_value_type(persistent_node_ptr node, Tuple &&args,
3029  index_sequence<I...>)
3030  {
3031  node_ptr new_node = node.get();
3032 
3033  node_allocator_traits::construct(
3034  _node_allocator, new_node->get(),
3035  std::get<I>(std::forward<Tuple>(args))...);
3036  }
3037 
3043  void
3045  {
3046  dummy_head = creates_dummy_node(MAX_LEVEL);
3047  }
3048 
3049  template <typename Tuple, size_t... I>
3050  persistent_node_ptr
3051  creates_dummy_node(Tuple &&args, index_sequence<I...>)
3052  {
3053  return creates_dummy_node(
3054  std::get<I>(std::forward<Tuple>(args))...);
3055  }
3056 
3066  template <typename... Args>
3067  persistent_node_ptr
3068  creates_dummy_node(size_type height, Args &&... args)
3069  {
3070  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3071  size_type sz = calc_node_size(height);
3072 
3074  node_allocator_traits::allocate(_node_allocator, sz)
3075  .raw();
3076 
3077  assert(n != nullptr);
3078 
3079  node_allocator_traits::construct(_node_allocator, n.get(),
3080  height,
3081  std::forward<Args>(args)...);
3082 
3083  return n;
3084  }
3085 
3086  template <bool is_dummy = false>
3087  void
3088  delete_node(persistent_node_ptr &node)
3089  {
3090  assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3091  node_ptr n = node.get();
3092  size_type sz = calc_node_size(n->height());
3093 
3094  /* Destroy value */
3095  if (!is_dummy)
3096  node_allocator_traits::destroy(_node_allocator,
3097  n->get());
3098  /* Destroy node */
3099  node_allocator_traits::destroy(_node_allocator, n);
3100  /* Deallocate memory */
3101  deallocate_node(node, sz);
3102  node = nullptr;
3103  }
3104 
3105  void
3106  deallocate_node(persistent_node_ptr &node, size_type sz)
3107  {
3108  /*
3109  * Each node object has different size which depends on number
3110  * of layers the node is linked. Therefore, allocate/deallocate
3111  * just a raw byte array. persistent_ptr<uint8_t> is used as a
3112  * pointer to raw array of bytes.
3113  */
3115  node.to_persistent_ptr().raw();
3116  node_allocator_traits::deallocate(_node_allocator, tmp, sz);
3117  }
3118 
3119  void
3120  delete_dummy_head()
3121  {
3122  assert(dummy_head != nullptr);
3123  delete_node<true>(dummy_head);
3124  assert(dummy_head == nullptr);
3125  }
3126 
3127  iterator
3128  get_iterator(const_iterator it)
3129  {
3130  return iterator(
3131  const_cast<typename iterator::node_ptr>(it.node));
3132  }
3133 
3135  void
3137  {
3138  int64_t last_run_size = 0;
3139  obj::pool_base pop = get_pool_base();
3140 
3141  for (auto &tls_entry : tls_data) {
3142  persistent_node_ptr &node = tls_entry.ptr;
3143  auto &size_diff = tls_entry.size_diff;
3144  if (node) {
3145  /*
3146  * We are completing inserts which were in
3147  * progress before the crash because readers
3148  * might saw incompleted inserts before the
3149  * crash. We set the in_progress flag inside
3150  * try_insert_node function when we locked the
3151  * predecessors for the new node, therefore,
3152  * only single node with the same key might have
3153  * the in_progress status.
3154  */
3155  if (tls_entry.insert_stage == in_progress) {
3156  complete_insert(tls_entry);
3157  } else {
3158  obj::transaction::run(pop, [&] {
3159  --(tls_entry.size_diff);
3160  delete_node(node);
3161  node = nullptr;
3162  });
3163  }
3164  }
3165 
3166  assert(node == nullptr);
3167 
3168  last_run_size += size_diff;
3169  }
3170 
3171  /* Make sure that on_init_size + last_run_size >= 0 */
3172  assert(last_run_size >= 0 ||
3173  on_init_size >
3174  static_cast<size_type>(std::abs(last_run_size)));
3175  obj::transaction::run(pop, [&] {
3176  tls_data.clear();
3177  on_init_size += static_cast<size_t>(last_run_size);
3178  });
3179  _size = on_init_size;
3180 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
3181  VALGRIND_PMC_DO_FLUSH(&_size, sizeof(_size));
3182 #endif
3183  }
3184 
3185  void
3186  complete_insert(tls_entry_type &tls_entry)
3187  {
3188  persistent_node_ptr &node = tls_entry.ptr;
3189  assert(node != nullptr);
3190  assert(tls_entry.insert_stage == in_progress);
3191  prev_array_type prev_nodes;
3192  next_array_type next_nodes;
3193  node_ptr n = node.get();
3194  const key_type &key = get_key(n);
3195  size_type height = n->height();
3196 
3197  fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
3198  obj::pool_base pop = get_pool_base();
3199 
3200  /* Node was partially linked */
3201  for (size_type level = 0; level < height; ++level) {
3202  assert(prev_nodes[level]->height() > level);
3203  assert(prev_nodes[level]->next(level) ==
3204  next_nodes[level]);
3205 
3206  if (prev_nodes[level]->next(level) != node) {
3207  /* Otherwise, node already linked on
3208  * this layer */
3209  assert(n->next(level) == next_nodes[level]);
3210  prev_nodes[level]->set_next(pop, level, node);
3211  }
3212  }
3213 
3214  node = nullptr;
3215  pop.persist(&node, sizeof(node));
3216  }
3217 
3218  struct not_greater_compare {
3219  const key_compare &my_less_compare;
3220 
3221  not_greater_compare(const key_compare &less_compare)
3222  : my_less_compare(less_compare)
3223  {
3224  }
3225 
3226  template <typename K1, typename K2>
3227  bool
3228  operator()(const K1 &first, const K2 &second) const
3229  {
3230  return !my_less_compare(second, first);
3231  }
3232  };
3233 
3234  const uint64_t pool_uuid = pmemobj_oid(this).pool_uuid_lo;
3235  node_allocator_type _node_allocator;
3236  key_compare _compare;
3237  random_level_generator_type _rnd_generator;
3238  persistent_node_ptr dummy_head;
3239 
3240  enumerable_thread_specific<tls_entry_type> tls_data;
3241 
3242  std::atomic<size_type> _size;
3243 
3250 }; /* class concurrent_skip_list */
3251 
3252 template <typename Key, typename Value, typename KeyCompare,
3253  typename RND_GENERATOR, typename Allocator, bool AllowMultimapping,
3254  size_t MAX_LEVEL>
3255 class map_traits {
3256 public:
3257  static constexpr size_t max_level = MAX_LEVEL;
3258  using random_generator_type = RND_GENERATOR;
3259  using key_type = Key;
3260  using mapped_type = Value;
3261  using compare_type = KeyCompare;
3262  using value_type = pair<const key_type, mapped_type>;
3263  using reference = value_type &;
3264  using const_reference = const value_type &;
3265  using allocator_type = Allocator;
3266 
3273  constexpr static bool allow_multimapping = AllowMultimapping;
3274 
3275  static const key_type &
3276  get_key(const_reference val)
3277  {
3278  return val.first;
3279  }
3280 }; /* class map_traits */
3281 
3282 } /* namespace detail */
3283 } /* namespace pmem */
3284 
3285 #endif /* PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP */
pmem::detail::concurrent_skip_list::fill_prev_next_arrays
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:2475
pmem::detail::concurrent_skip_list::find_higher
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
pmem::detail::concurrent_skip_list::find_lower
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
pmem::detail::allocator_swap
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
pmem::detail::concurrent_skip_list::internal_extract
std::pair< persistent_node_ptr, persistent_node_ptr > internal_extract(const_iterator it)
Definition: concurrent_skip_list_impl.hpp:2881
pmem::detail::concurrent_skip_list::internal_get_bound
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,...
Definition: concurrent_skip_list_impl.hpp:2811
pmem::detail::concurrent_skip_list::unsafe_erase
iterator unsafe_erase(const_iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1265
pmem::obj::transaction::commit
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:353
pmem::detail::concurrent_skip_list::upper_bound
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
pmem::detail::concurrent_skip_list::try_emplace
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
pmem::obj::mutex
Persistent memory resident mutex implementation.
Definition: mutex.hpp:31
pmem::detail::concurrent_skip_list::emplace_hint
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
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list(const concurrent_skip_list &other)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:614
pmem::detail::concurrent_skip_list::insert
std::pair< iterator, bool > insert(P &&value)
Inserts value.
Definition: concurrent_skip_list_impl.hpp:933
pmem::detail::concurrent_skip_list::internal_insert_node
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:2633
pmem::detail::concurrent_skip_list::find_lower
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
pmem::detail::concurrent_skip_list::key_comp
key_compare & key_comp()
Returns a reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2282
pmem::detail::concurrent_skip_list::cend
const_iterator cend() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2074
pmem::pool_error
Custom pool error class.
Definition: pexceptions.hpp:47
pmem::obj::experimental::self_relative_ptr::get
element_type * get() const noexcept
Get the direct pointer.
Definition: self_relative_ptr.hpp:159
pmem::detail::concurrent_skip_list::lower_bound
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
pmem::detail::concurrent_skip_list::cbegin
const_iterator cbegin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2035
pmem::obj::pool_base::persist
void persist(const void *addr, size_t len) noexcept
Performs persist operation on a given chunk of memory.
Definition: pool.hpp:283
pmem
Persistent memory namespace.
Definition: allocation_flag.hpp:15
pmem::detail::concurrent_skip_list::contains
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
pmem::detail::concurrent_skip_list::unsafe_erase
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
pmem::obj::p::swap
void swap(p &other)
Swaps two p objects of the same type.
Definition: p.hpp:140
pmem::detail::concurrent_skip_list::find_higher_eq
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
common.hpp
Commonly used functionality.
pmem::detail::concurrent_skip_list::find
iterator find(const key_type &key)
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1836
template_helpers.hpp
Commonly used SFINAE helpers.
pmem::detail::concurrent_skip_list::try_emplace
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
pmem::detail::concurrent_skip_list::operator=
concurrent_skip_list & operator=(concurrent_skip_list &&other)
Move assignment operator.
Definition: concurrent_skip_list_impl.hpp:838
pmem::detail::allocator_move_assignment
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
pmem::obj::transaction::snapshot
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:496
pmem::detail::concurrent_skip_list::concurrent_skip_list
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
pmem::detail::concurrent_skip_list::operator=
concurrent_skip_list & operator=(const concurrent_skip_list &other)
Copy assignment operator.
Definition: concurrent_skip_list_impl.hpp:796
pmem::obj::experimental::self_relative_ptr
Persistent self-relative pointer class.
Definition: self_relative_ptr.hpp:44
pmem::obj::operator++
p< T > & operator++(p< T > &pp)
Prefix increment operator overload.
Definition: pext.hpp:48
pmem::detail::concurrent_skip_list::equal_range
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:2195
pmem::detail::concurrent_skip_list::find_lower
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
pmem::obj::operator==
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
pmem::detail::concurrent_skip_list::end
iterator end()
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2048
pmem::detail::concurrent_skip_list::create_dummy_head
void create_dummy_head()
Creates dummy head.
Definition: concurrent_skip_list_impl.hpp:3044
pmem::detail::concurrent_skip_list::insert
std::pair< iterator, bool > insert(value_type &&value)
Inserts value using move semantic.
Definition: concurrent_skip_list_impl.hpp:955
pmem::obj::p< difference_type >
pmem::detail::concurrent_skip_list::lower_bound
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
pmem::detail::concurrent_skip_list::find_insert_pos
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:2450
pmem::detail::concurrent_skip_list::contains
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
pmem::detail::concurrent_skip_list::find_lower_eq
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
pmem::detail::concurrent_skip_list
Persistent memory aware implementation of the concurrent skip list.
Definition: concurrent_skip_list_impl.hpp:479
pmem::detail::concurrent_skip_list::size
size_type size() const
Returns the number of elements in the container, i.e.
Definition: concurrent_skip_list_impl.hpp:2086
pool.hpp
C++ pmemobj pool.
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list(const key_compare &comp, const allocator_type &alloc=allocator_type())
Constructs an empty container.
Definition: concurrent_skip_list_impl.hpp:553
pmem::detail::concurrent_skip_list::equal_range
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:2169
pmem::detail::concurrent_skip_list::find
const_iterator find(const key_type &key) const
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1850
pmem::detail::concurrent_skip_list::find_higher_eq
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
pmem::obj::transaction::run
static void run(pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:406
pmem::detail::concurrent_skip_list::emplace
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
pmem::obj::swap
void swap(pmem::obj::array< T, N > &lhs, pmem::obj::array< T, N > &rhs)
Non-member swap function.
Definition: array.hpp:880
pmem::detail::allocator_copy_assignment
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
pmem::detail::concurrent_skip_list::free_data
void free_data()
Should be called before concurrent_skip_list destructor is called.
Definition: concurrent_skip_list_impl.hpp:750
pmem::detail::concurrent_skip_list::count
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
pmem::obj::operator!=
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
pmem::detail::concurrent_skip_list::internal_get_bound
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,...
Definition: concurrent_skip_list_impl.hpp:2785
pmem::detail::concurrent_skip_list::upper_bound
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
pmem::detail::concurrent_skip_list::try_insert_node
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:2667
pmem::detail::concurrent_skip_list::~concurrent_skip_list
~concurrent_skip_list()
Destructor.
Definition: concurrent_skip_list_impl.hpp:771
pmem::detail::concurrent_skip_list::try_emplace
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
pmem::detail::concurrent_skip_list::operator=
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
pmem::detail::concurrent_skip_list::key_comp
const key_compare & key_comp() const
Returns a const reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2271
pmem::detail::concurrent_skip_list::runtime_initialize
void runtime_initialize()
Intialize concurrent_skip_list after process restart.
Definition: concurrent_skip_list_impl.hpp:729
transaction.hpp
C++ pmemobj transactions.
pmem::detail::concurrent_skip_list::creates_dummy_node
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:3068
pmem::detail::concurrent_skip_list::upper_bound
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::detail::concurrent_skip_list::insert
void insert(InputIterator first, InputIterator last)
Inserts elements from range [first, last).
Definition: concurrent_skip_list_impl.hpp:1029
pmem::detail::concurrent_skip_list::find
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
pmem::detail::concurrent_skip_list::internal_insert
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:2594
pmem::detail::enumerable_thread_specific::local
reference local()
Returns data reference for the current thread.
Definition: enumerable_thread_specific.hpp:305
pmem::detail::concurrent_skip_list::upper_bound
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
pmem::detail::concurrent_skip_list::count
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
pmem::detail::concurrent_skip_list::tls_restore
void tls_restore()
Process any information which was saved to tls and clears tls.
Definition: concurrent_skip_list_impl.hpp:3136
pmem::obj::persistent_ptr
Persistent pointer class.
Definition: persistent_ptr.hpp:152
pmem::detail::concurrent_skip_list::find_lower_eq
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
pmem::detail::concurrent_skip_list::begin
iterator begin()
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2011
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list(concurrent_skip_list &&other)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:676
pmem::detail::concurrent_skip_list::insert
std::pair< iterator, bool > insert(const value_type &value)
Inserts value in a thread-safe way.
Definition: concurrent_skip_list_impl.hpp:906
pmem::detail::concurrent_skip_list::lower_bound
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
pmem::obj::get
T & get(pmem::obj::array< T, N > &a)
Non-member get function.
Definition: array.hpp:890
pmem::detail::concurrent_skip_list::insert
void insert(std::initializer_list< value_type > ilist)
Inserts elements from initializer list ilist.
Definition: concurrent_skip_list_impl.hpp:1049
pmem::detail::concurrent_skip_list::check_prev_array
bool check_prev_array(const prev_array_type &prevs, size_type height)
Used only inside asserts.
Definition: concurrent_skip_list_impl.hpp:2735
pmem::detail::concurrent_skip_list::insert
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
pmem::detail::concurrent_skip_list::empty
bool empty() const
Checks if the container has no elements, i.e.
Definition: concurrent_skip_list_impl.hpp:2111
pmem::detail::concurrent_skip_list::lower_bound
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
pmem::detail::concurrent_skip_list::find_higher
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
pmem::detail::concurrent_skip_list::internal_find_position
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:2421
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list(const concurrent_skip_list &other, const allocator_type &alloc)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:646
pmem::detail::enumerable_thread_specific::clear
void clear()
Removes all elements from the container.
Definition: enumerable_thread_specific.hpp:345
pmem::detail::concurrent_skip_list::find_lower_eq
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
self_relative_ptr.hpp
Persistent self-relative smart pointer.
pmem::detail::concurrent_skip_list::internal_unsafe_emplace
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:2557
pmem::detail::concurrent_skip_list::find_lower_eq
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
life.hpp
Functions for destroying arrays.
pmem::detail::concurrent_skip_list::equal_range
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:2227
pmem::detail::concurrent_skip_list::find_lower
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
pmem::detail::concurrent_skip_list::max_size
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
pmem::transaction_scope_error
Custom transaction error class.
Definition: pexceptions.hpp:163
pmem::detail::concurrent_skip_list::unsafe_erase
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
pmem::detail::concurrent_skip_list::end
const_iterator end() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2061
pmem::detail::concurrent_skip_list::find_higher
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
pmem::detail::concurrent_skip_list::on_init_size
obj::p< size_type > on_init_size
This variable holds real size after the skip list is initialized.
Definition: concurrent_skip_list_impl.hpp:3249
pmem::detail::concurrent_skip_list::find_higher
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
pmem::detail::concurrent_skip_list::clear
void clear()
Erases all elements from the container transactionally.
Definition: concurrent_skip_list_impl.hpp:1977
pmem::detail::concurrent_skip_list::equal_range
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:2259
pmem::detail::concurrent_skip_list::unsafe_erase
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
pmem::obj::pool_base
The non-template pool base class.
Definition: pool.hpp:46
pmem::detail::concurrent_skip_list::insert
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
pmem::detail::concurrent_skip_list::find_higher_eq
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
pmem::detail::concurrent_skip_list::find
iterator find(const K &x)
Finds an element with key that compares equivalent to the value x.
Definition: concurrent_skip_list_impl.hpp:1871
pmem::detail::concurrent_skip_list::random_level
size_type random_level()
Generate random level.
Definition: concurrent_skip_list_impl.hpp:2983
pmem::detail::concurrent_skip_list::swap
void swap(concurrent_skip_list &other)
XXX: Implement get_allocator() interface.
Definition: concurrent_skip_list_impl.hpp:2129
pmem::detail::concurrent_skip_list::internal_get_biggest_less_than
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,...
Definition: concurrent_skip_list_impl.hpp:2837
persistent_ptr.hpp
Persistent smart pointer.
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list()
Default constructor.
Definition: concurrent_skip_list_impl.hpp:531
pmem::detail::concurrent_skip_list::concurrent_skip_list
concurrent_skip_list(concurrent_skip_list &&other, const allocator_type &alloc)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:705
pmem::detail::concurrent_skip_list::begin
const_iterator begin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2023
pmem::obj::transaction::manual
C++ manual scope transaction class.
Definition: transaction.hpp:84
enumerable_thread_specific.hpp
A persistent version of thread-local storage.
pmem::detail::concurrent_skip_list::unsafe_erase
iterator unsafe_erase(iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1241
pmem::detail::concurrent_skip_list::create_node
persistent_node_ptr create_node(Args &&... args)
Creates new node.
Definition: concurrent_skip_list_impl.hpp:2998
pmem::detail::concurrent_skip_list::get_pool_base
obj::pool_base get_pool_base() const
Get the persistent memory pool where hashmap resides.
Definition: concurrent_skip_list_impl.hpp:2934
mutex.hpp
Pmem-resident mutex.
pmem::detail::concurrent_skip_list::check_tx_stage_work
void check_tx_stage_work() const
Private helper function.
Definition: concurrent_skip_list_impl.hpp:2314
pmem::detail::concurrent_skip_list::find_higher_eq
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