4 #ifndef PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
5 #define PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
14 #include <type_traits>
19 #include <libpmemobj++/detail/pair.hpp>
26 #include <libpmemobj++/experimental/atomic_self_relative_ptr.hpp>
47 try_insert_node_finish_marker()
56 template <
typename MyAlloc,
typename OtherAlloc>
61 my_allocator = other_allocator;
68 template <
typename MyAlloc,
typename OtherAlloc>
78 template <
typename MyAlloc,
typename OtherAlloc>
83 my_allocator = std::move(other_allocator);
90 template <
typename MyAlloc,
typename OtherAlloc>
100 template <
typename MyAlloc,
typename OtherAlloc>
105 std::swap(my_allocator, other_allocator);
112 template <
typename MyAlloc,
typename OtherAlloc>
119 typename LockType = std::unique_lock<Mutex>>
120 class skip_list_node {
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 *;
130 using atomic_node_pointer = std::atomic<node_pointer>;
131 using mutex_type = Mutex;
132 using lock_type = LockType;
134 skip_list_node(size_type levels) : height_(levels)
136 for (size_type lev = 0; lev < height_; ++lev)
137 detail::create<atomic_node_pointer>(&get_next(lev),
140 assert(height() == levels);
141 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
146 for (size_type lev = 0; lev < height_; ++lev) {
147 VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
148 sizeof(get_next(lev)));
153 skip_list_node(size_type levels,
const node_pointer *new_nexts)
156 for (size_type lev = 0; lev < height_; ++lev)
157 detail::create<atomic_node_pointer>(&get_next(lev),
160 assert(height() == levels);
161 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
166 for (size_type lev = 0; lev < height_; ++lev) {
167 VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
168 sizeof(get_next(lev)));
175 for (size_type lev = 0; lev < height_; ++lev)
176 detail::destroy<atomic_node_pointer>(get_next(lev));
179 skip_list_node(
const skip_list_node &) =
delete;
181 skip_list_node &operator=(
const skip_list_node &) =
delete;
202 next(size_type level)
const
204 assert(level < height());
205 return get_next(level).load(std::memory_order_acquire);
213 set_next_tx(size_type level, node_pointer next)
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);
223 set_next(obj::pool_base pop, size_type level, node_pointer next)
225 assert(level < height());
226 auto &node = get_next(level);
227 node.store(next, std::memory_order_release);
228 pop.persist(&node,
sizeof(node));
232 set_nexts(
const node_pointer *new_nexts, size_type h)
234 assert(h == height());
235 auto *nexts = get_nexts();
237 for (size_type i = 0; i < h; i++) {
238 nexts[i].store(new_nexts[i], std::memory_order_relaxed);
243 set_nexts(obj::pool_base pop,
const node_pointer *new_nexts,
246 set_nexts(new_nexts, h);
248 auto *nexts = get_nexts();
249 pop.persist(nexts,
sizeof(nexts[0]) * h);
262 return lock_type(mutex);
266 atomic_node_pointer *
269 return reinterpret_cast<atomic_node_pointer *
>(
this + 1);
272 atomic_node_pointer &
273 get_next(size_type level)
275 auto *arr = get_nexts();
279 const atomic_node_pointer &
280 get_next(size_type level)
const
283 reinterpret_cast<const atomic_node_pointer *
>(
this + 1);
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 *,
299 friend class skip_list_iterator<node_type, true>;
302 using value_type =
typename node_type::value_type;
303 using iterator_category = std::forward_iterator_tag;
304 using difference_type = std::ptrdiff_t;
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 *,
312 skip_list_iterator() : node(nullptr)
317 skip_list_iterator(
const skip_list_iterator &other) : node(other.node)
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)
329 reference operator*()
const
331 return *(node->get());
334 pointer operator->()
const
342 assert(node !=
nullptr);
343 node = node->next(0).get();
350 skip_list_iterator tmp = *
this;
356 operator=(
const skip_list_iterator &other)
363 explicit skip_list_iterator(node_type *n) : node(n)
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)
375 template <
typename Traits>
376 friend class concurrent_skip_list;
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);
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);
387 template <
typename T,
bool M,
bool U>
389 operator==(
const skip_list_iterator<T, M> &lhs,
390 const skip_list_iterator<T, U> &rhs)
392 return lhs.node == rhs.node;
395 template <
typename T,
bool M,
bool U>
397 operator!=(
const skip_list_iterator<T, M> &lhs,
398 const skip_list_iterator<T, U> &rhs)
400 return lhs.node != rhs.node;
403 struct default_random_generator {
404 using gen_type = std::mt19937_64;
405 using result_type =
typename gen_type::result_type;
410 static thread_local gen_type engine(
411 static_cast<size_t>(time(0)));
416 static constexpr result_type
419 return gen_type::min();
422 static constexpr result_type
425 return gen_type::max();
429 template <
typename RndGenerator,
size_t MAX_LEVEL>
430 class geometric_level_generator {
432 using rnd_generator_type = RndGenerator;
434 static constexpr
size_t max_level = MAX_LEVEL;
441 static rnd_generator_type gen;
444 static thread_local std::geometric_distribution<size_t> d;
446 return (d(gen) % MAX_LEVEL) + 1;
478 template <
typename Traits>
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>;
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;
496 using list_node_type = skip_list_node<value_type>;
498 using iterator = skip_list_iterator<list_node_type, false>;
499 using const_iterator = skip_list_iterator<list_node_type, true>;
501 static constexpr size_type MAX_LEVEL = traits_type::max_level;
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 *;
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>;
520 static constexpr
bool allow_multimapping =
521 traits_type::allow_multimapping;
554 const key_compare &comp,
555 const allocator_type &alloc = allocator_type())
556 : _node_allocator(alloc), _compare(comp)
585 template <
class InputIt>
587 const key_compare &comp = key_compare(),
588 const allocator_type &alloc = allocator_type())
589 : _node_allocator(alloc), _compare(comp)
593 while (first != last)
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)
623 internal_copy(other);
624 assert(_size == other._size);
647 const allocator_type &alloc)
648 : _node_allocator(alloc),
649 _compare(other._compare),
650 _rnd_generator(other._rnd_generator)
654 internal_copy(other);
655 assert(_size == other._size);
677 : _node_allocator(
std::move(other._node_allocator)),
678 _compare(other._compare),
679 _rnd_generator(other._rnd_generator)
683 internal_move(std::move(other));
706 const allocator_type &alloc)
707 : _node_allocator(alloc),
708 _compare(other._compare),
709 _rnd_generator(other._rnd_generator)
713 if (alloc == other.get_allocator()) {
714 internal_move(std::move(other));
717 internal_copy(std::make_move_iterator(other.begin()),
718 std::make_move_iterator(other.end()));
733 assert(this->
size() ==
734 size_type(std::distance(this->
begin(), this->
end())));
752 if (dummy_head ==
nullptr)
803 using pocca_t =
typename node_allocator_traits::
804 propagate_on_container_copy_assignment;
807 other._node_allocator,
809 _compare = other._compare;
810 _rnd_generator = other._rnd_generator;
812 internal_copy(other);
845 using pocma_t =
typename node_allocator_traits::
846 propagate_on_container_move_assignment;
848 if (pocma_t::value ||
849 _node_allocator == other._node_allocator) {
852 other._node_allocator,
854 _compare = other._compare;
855 _rnd_generator = other._rnd_generator;
856 internal_move(std::move(other));
859 std::make_move_iterator(other.begin()),
860 std::make_move_iterator(other.end()));
883 for (
auto it = il.begin(); it != il.end(); ++it)
905 std::pair<iterator, bool>
929 template <
typename P,
930 typename std::enable_if<
931 std::is_constructible<value_type, P &&>::value>::type>
932 std::pair<iterator, bool>
935 return emplace(std::forward<P>(value));
954 std::pair<iterator, bool>
978 insert(const_iterator hint, const_reference value)
981 return insert(value).first;
1004 template <
typename P,
1005 typename std::enable_if<
1006 std::is_constructible<value_type, P &&>::value>::type>
1027 template <
typename InputIterator>
1029 insert(InputIterator first, InputIterator last)
1031 for (InputIterator it = first; it != last; ++it)
1049 insert(std::initializer_list<value_type> ilist)
1051 insert(ilist.begin(), ilist.end());
1081 template <
typename... Args>
1082 std::pair<iterator, bool>
1085 return internal_emplace(std::forward<Args>(args)...);
1118 template <
typename... Args>
1123 return emplace(std::forward<Args>(args)...).first;
1149 template <
typename... Args>
1150 std::pair<iterator, bool>
1153 return internal_try_emplace(k, std::forward<Args>(args)...);
1179 template <
typename... Args>
1180 std::pair<iterator, bool>
1183 return internal_try_emplace(std::move(k),
1184 std::forward<Args>(args)...);
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
1220 return internal_try_emplace(std::forward<K>(k),
1221 std::forward<Args>(args)...);
1244 auto &size_diff = tls_data.
local().size_diff;
1245 return internal_erase(pos, size_diff);
1289 auto &size_diff = tls_data.
local().size_diff;
1292 while (first != last) {
1293 first = internal_erase(first, size_diff);
1297 return get_iterator(first);
1315 std::pair<iterator, iterator> range =
equal_range(key);
1316 size_type sz =
static_cast<size_type
>(
1317 std::distance(range.first, range.second));
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,
1350 std::pair<iterator, iterator> range =
equal_range(key);
1351 size_type sz =
static_cast<size_type
>(
1352 std::distance(range.first, range.second));
1402 template <
typename K,
1403 typename =
typename std::enable_if<
1404 has_is_transparent<key_compare>::value, K>::type>
1424 template <
typename K,
1425 typename =
typename std::enable_if<
1426 has_is_transparent<key_compare>::value, K>::type>
1479 template <
typename K,
1480 typename =
typename std::enable_if<
1481 has_is_transparent<key_compare>::value, K>::type>
1502 template <
typename K,
1503 typename =
typename std::enable_if<
1504 has_is_transparent<key_compare>::value, K>::type>
1556 template <
typename K,
1557 typename =
typename std::enable_if<
1558 has_is_transparent<key_compare>::value, K>::type>
1578 template <
typename K,
1579 typename =
typename std::enable_if<
1580 has_is_transparent<key_compare>::value, K>::type>
1632 template <
typename K,
1633 typename =
typename std::enable_if<
1634 has_is_transparent<key_compare>::value, K>::type>
1654 template <
typename K,
1655 typename =
typename std::enable_if<
1656 has_is_transparent<key_compare>::value, K>::type>
1678 const_cast<typename iterator::node_ptr>(it.node));
1710 template <
typename K,
1711 typename =
typename std::enable_if<
1712 has_is_transparent<key_compare>::value, K>::type>
1718 const_cast<typename iterator::node_ptr>(it.node));
1734 template <
typename K,
1735 typename =
typename std::enable_if<
1736 has_is_transparent<key_compare>::value, K>::type>
1757 key, not_greater_compare(_compare));
1759 const_cast<typename iterator::node_ptr>(it.node));
1776 key, not_greater_compare(_compare));
1792 template <
typename K,
1793 typename =
typename std::enable_if<
1794 has_is_transparent<key_compare>::value, K>::type>
1799 key, not_greater_compare(_compare));
1801 const_cast<typename iterator::node_ptr>(it.node));
1817 template <
typename K,
1818 typename =
typename std::enable_if<
1819 has_is_transparent<key_compare>::value, K>::type>
1824 key, not_greater_compare(_compare));
1838 return internal_find(key);
1852 return internal_find(key);
1867 template <
typename K,
1868 typename =
typename std::enable_if<
1869 has_is_transparent<key_compare>::value, K>::type>
1873 return internal_find(x);
1888 template <
typename K,
1889 typename =
typename std::enable_if<
1890 has_is_transparent<key_compare>::value, K>::type>
1894 return internal_find(x);
1909 return internal_count(key);
1924 template <
typename K,
1925 typename =
typename std::enable_if<
1926 has_is_transparent<key_compare>::value, K>::type>
1930 return internal_count(key);
1959 template <
typename K,
1960 typename =
typename std::enable_if<
1961 has_is_transparent<key_compare>::value, K>::type>
1979 assert(dummy_head->height() > 0);
1986 assert(current->height() > 0);
1988 delete_node(current);
1992 node_ptr head = dummy_head.
get();
1993 for (size_type i = 0; i < head->height(); ++i) {
1994 head->set_next_tx(i,
nullptr);
2013 return iterator(dummy_head.
get()->next(0).get());
2025 return const_iterator(dummy_head.
get()->next(0).get());
2037 return const_iterator(dummy_head.
get()->next(0).get());
2050 return iterator(
nullptr);
2063 return const_iterator(
nullptr);
2076 return const_iterator(
nullptr);
2088 return _size.load(std::memory_order_relaxed);
2101 return std::numeric_limits<difference_type>::max();
2124 return allocator_type(_node_allocator);
2139 using pocs_t =
typename node_allocator_traits::
2140 propagate_on_container_swap;
2144 std::swap(_rnd_generator, other._rnd_generator);
2145 std::swap(dummy_head, other.dummy_head);
2150 (
size_t *)&(other._size));
2151 _size = other._size.exchange(_size,
2152 std::memory_order_relaxed);
2175 std::pair<iterator, iterator>
2178 return std::pair<iterator, iterator>(
lower_bound(key),
2201 std::pair<const_iterator, const_iterator>
2204 return std::pair<const_iterator, const_iterator>(
2230 template <
typename K,
2231 typename =
typename std::enable_if<
2232 has_is_transparent<key_compare>::value, K>::type>
2233 std::pair<iterator, iterator>
2236 return std::pair<iterator, iterator>(
lower_bound(x),
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>
2268 return std::pair<const_iterator, const_iterator>(
2296 enum insert_stage_type : uint8_t { not_started = 0, in_progress = 1 };
2301 struct tls_entry_type {
2302 persistent_node_ptr ptr;
2306 char reserved[64 -
sizeof(decltype(ptr)) -
2307 sizeof(decltype(size_diff)) -
2308 sizeof(decltype(insert_stage))];
2310 static_assert(
sizeof(tls_entry_type) == 64,
2311 "The size of tls_entry_type should be 64 bytes.");
2323 if (pmemobj_tx_stage() != TX_STAGE_WORK)
2325 "Function called out of transaction scope.");
2332 if (pmemobj_tx_stage() != TX_STAGE_NONE)
2334 "Function called inside transaction scope.");
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();
2357 _size.store(other._size.load(std::memory_order_relaxed),
2358 std::memory_order_relaxed);
2362 static const_reference
2363 get_val(const_node_ptr n)
2376 static const key_type &
2377 get_key(const_node_ptr n)
2380 return traits_type::get_key(get_val(n));
2383 template <
typename K>
2385 internal_find(
const K &key)
2388 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2393 template <
typename K>
2395 internal_find(
const K &key)
const
2398 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2403 template <
typename K>
2405 internal_count(
const K &key)
const
2407 if (allow_multimapping) {
2408 std::pair<const_iterator, const_iterator> range =
2410 return static_cast<size_type
>(
2411 std::distance(range.first, range.second));
2413 return (
find(key) ==
end()) ? size_type(0) : size_type(1);
2426 template <
typename K,
typename po
inter_type,
typename comparator>
2429 const K &key,
const comparator &cmp)
const
2431 assert(level < prev->height());
2433 pointer_type curr = next.
get();
2435 while (curr && cmp(get_key(curr), key)) {
2437 assert(level < prev->height());
2438 next = prev->next(level);
2455 template <
typename K>
2458 next_array_type &next_nodes,
const K &key)
2460 if (allow_multimapping) {
2462 not_greater_compare(_compare));
2480 template <
typename K,
typename comparator>
2483 next_array_type &next_nodes,
const K &key,
2484 const comparator &cmp)
2486 node_ptr prev = dummy_head.
get();
2487 prev_nodes.fill(prev);
2488 next_nodes.fill(
nullptr);
2490 for (size_type h = prev->height(); h > 0; --h) {
2493 prev_nodes[h - 1] = prev;
2494 next_nodes[h - 1] = next;
2498 template <
typename K,
typename... Args>
2499 std::pair<iterator, bool>
2500 internal_try_emplace(K &&key, Args &&... args)
2503 key, std::piecewise_construct,
2504 std::forward_as_tuple(std::forward<K>(key)),
2505 std::forward_as_tuple(std::forward<Args>(args)...));
2508 template <
typename... Args>
2509 std::pair<iterator, bool>
2510 internal_emplace(Args &&... args)
2513 tls_entry_type &tls_entry = tls_data.
local();
2517 assert(tls_entry.ptr ==
nullptr);
2520 ++tls_entry.size_diff;
2521 tls_entry.insert_stage = not_started;
2524 node_ptr n = tls_entry.ptr.get();
2525 size_type height = 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);
2534 n->set_nexts(pop, next_nodes.data(), height);
2536 tls_entry.insert_stage = in_progress;
2537 pop.
persist(&(tls_entry.insert_stage),
2538 sizeof(tls_entry.insert_stage));
2540 return tls_entry.ptr;
2543 if (!insert_result.second) {
2544 assert(tls_entry.ptr !=
nullptr);
2545 assert(tls_entry.insert_stage == not_started);
2548 --tls_entry.size_diff;
2549 delete_node(tls_entry.ptr);
2550 tls_entry.ptr =
nullptr;
2554 assert(tls_entry.ptr ==
nullptr);
2555 return insert_result;
2562 template <
typename... Args>
2563 std::pair<iterator, bool>
2571 node_ptr n = new_node.
get();
2572 size_type height = n->height();
2576 [&](
const next_array_type &next_nodes)
2578 assert(new_node !=
nullptr);
2580 n->set_nexts(next_nodes.data(), height);
2585 if (insert_result.second) {
2588 assert(new_node !=
nullptr);
2590 delete_node(new_node);
2593 return insert_result;
2599 template <
typename K,
typename... Args>
2600 std::pair<iterator, bool>
2604 tls_entry_type &tls_entry = tls_data.
local();
2605 assert(tls_entry.ptr ==
nullptr);
2611 [&](
const next_array_type &next_nodes)
2617 std::forward_as_tuple(
2618 height, next_nodes.data()),
2619 std::forward_as_tuple(
2620 std::forward<Args>(args)...));
2622 ++(tls_entry.size_diff);
2623 tls_entry.insert_stage = in_progress;
2626 assert(tls_entry.ptr !=
nullptr);
2627 return tls_entry.ptr;
2630 assert(tls_entry.ptr ==
nullptr);
2632 return insert_result;
2638 template <
typename K,
typename PrepareNode>
2639 std::pair<iterator, bool>
2641 PrepareNode &&prepare_new_node)
2643 prev_array_type prev_nodes;
2644 next_array_type next_nodes;
2645 node_ptr n =
nullptr;
2650 node_ptr next = next_nodes[0].get();
2651 if (next && !allow_multimapping &&
2652 !_compare(key, get_key(next))) {
2654 return std::pair<iterator, bool>(iterator(next),
2659 std::forward<PrepareNode>(
2660 prepare_new_node))) ==
2664 return std::pair<iterator, bool>(iterator(n),
true);
2672 template <
typename PrepareNode>
2675 const next_array_type &next_nodes, size_type height,
2676 PrepareNode &&prepare_new_node)
2678 assert(dummy_head->height() >= height);
2681 if (!try_lock_nodes(height, prev_nodes, next_nodes, locks)) {
2685 node_lock_type new_node_lock;
2688 assert(new_node !=
nullptr);
2689 node_ptr n = new_node.
get();
2697 new_node_lock = n->acquire();
2709 for (size_type level = 0; level < height; ++level) {
2710 assert(prev_nodes[level]->height() > level);
2711 assert(prev_nodes[level]->next(level) ==
2713 assert(prev_nodes[level]->next(level) ==
2715 prev_nodes[level]->set_next(pop, level, new_node);
2719 try_insert_node_finish_marker();
2726 pop.
persist(&new_node,
sizeof(new_node));
2729 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2730 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
2744 for (size_type l = 1; l < height; ++l) {
2745 if (prevs[l] == dummy_head.
get()) {
2749 assert(prevs[l - 1] != dummy_head.
get());
2750 assert(!_compare(get_key(prevs[l - 1]),
2751 get_key(prevs[l])));
2758 try_lock_nodes(size_type height, prev_array_type &prevs,
2759 const next_array_type &nexts, lock_array &locks)
2763 for (size_type l = 0; l < height; ++l) {
2764 if (l == 0 || prevs[l] != prevs[l - 1]) {
2765 locks[l] = prevs[l]->acquire();
2768 persistent_node_ptr next = prevs[l]->next(l);
2769 if (next != nexts[l])
2790 template <
typename K,
typename comparator>
2794 const_node_ptr prev = dummy_head.
get();
2795 assert(prev->height() > 0);
2798 for (size_type h = prev->height(); h > 0; --h) {
2802 return const_iterator(next.get());
2816 template <
typename K,
typename comparator>
2820 node_ptr prev = dummy_head.
get();
2821 assert(prev->height() > 0);
2824 for (size_type h = prev->height(); h > 0; --h) {
2828 return iterator(next.get());
2842 template <
typename K,
typename comparator>
2845 const comparator &cmp)
const
2847 const_node_ptr prev = dummy_head.
get();
2848 assert(prev->height() > 0);
2850 for (size_type h = prev->height(); h > 0; --h) {
2854 if (prev == dummy_head.
get())
2857 return const_iterator(prev);
2863 assert(pos !=
end());
2867 std::pair<persistent_node_ptr, persistent_node_ptr>
2868 extract_result(
nullptr,
nullptr);
2874 assert(extract_result.first !=
nullptr);
2875 delete_node(extract_result.first);
2881 return iterator(extract_result.second.get());
2887 std::pair<persistent_node_ptr, persistent_node_ptr>
2890 assert(dummy_head->height() > 0);
2891 assert(it !=
end());
2892 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2894 const key_type &key = traits_type::get_key(*it);
2896 prev_array_type prev_nodes;
2897 next_array_type next_nodes;
2901 node_ptr erase_node = next_nodes[0].get();
2902 assert(erase_node !=
nullptr);
2904 if (!_compare(key, get_key(erase_node))) {
2908 assert(erase_node == it.node);
2909 return internal_extract_node(prev_nodes, next_nodes,
2913 return std::pair<persistent_node_ptr, persistent_node_ptr>(
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)
2922 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2923 assert(erase_node !=
nullptr);
2924 for (size_type level = 0; level < erase_node->height();
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));
2932 return std::pair<persistent_node_ptr, persistent_node_ptr>(
2933 next_nodes[0], erase_node->next(0));
2943 PMEMobjpool *pop = pmemobj_pool_by_ptr(
this);
2950 internal_copy(other.
begin(), other.
end());
2953 template <
typename Iterator>
2955 internal_copy(Iterator first, Iterator last)
2957 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2959 prev_array_type prev_nodes;
2960 prev_nodes.fill(dummy_head.
get());
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();
2968 prev_nodes[level]->set_next_tx(level, new_node);
2969 prev_nodes[level] = n;
2981 assert(std::is_sorted(
2983 [&](
const value_type &lhs,
const value_type &rhs) {
2984 return lhs.first < rhs.first;
2992 return _rnd_generator();
2996 calc_node_size(size_type height)
2998 return sizeof(list_node_type) +
2999 height *
sizeof(
typename list_node_type::node_pointer);
3003 template <
typename... Args>
3010 std::forward_as_tuple(levels),
3011 std::forward_as_tuple(std::forward<Args>(args)...));
3014 template <
typename... NodeArgs,
typename... ValueArgs>
3017 std::tuple<ValueArgs...> &&value_args)
3019 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3021 persistent_node_ptr node = creates_dummy_node(
3022 std::forward<std::tuple<NodeArgs...>>(node_args),
3023 index_sequence_for<NodeArgs...>{});
3025 construct_value_type(
3027 std::forward<std::tuple<ValueArgs...>>(value_args),
3028 index_sequence_for<ValueArgs...>{});
3033 template <
typename Tuple,
size_t... I>
3035 construct_value_type(persistent_node_ptr node, Tuple &&args,
3036 index_sequence<I...>)
3038 node_ptr new_node = node.get();
3040 node_allocator_traits::construct(
3041 _node_allocator, new_node->get(),
3042 std::get<I>(std::forward<Tuple>(args))...);
3053 dummy_head = creates_dummy_node(MAX_LEVEL);
3056 template <
typename Tuple,
size_t... I>
3058 creates_dummy_node(Tuple &&args, index_sequence<I...>)
3060 return creates_dummy_node(
3061 std::get<I>(std::forward<Tuple>(args))...);
3073 template <
typename... Args>
3077 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3078 size_type sz = calc_node_size(height);
3081 node_allocator_traits::allocate(_node_allocator, sz)
3084 assert(n !=
nullptr);
3086 node_allocator_traits::construct(_node_allocator, n.
get(),
3088 std::forward<Args>(args)...);
3093 template <
bool is_dummy = false>
3095 delete_node(persistent_node_ptr &node)
3097 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3098 node_ptr n = node.get();
3099 size_type sz = calc_node_size(n->height());
3103 node_allocator_traits::destroy(_node_allocator,
3106 node_allocator_traits::destroy(_node_allocator, n);
3108 deallocate_node(node, sz);
3113 deallocate_node(persistent_node_ptr &node, size_type sz)
3122 node.to_persistent_ptr().
raw();
3123 node_allocator_traits::deallocate(_node_allocator, tmp, sz);
3129 assert(dummy_head !=
nullptr);
3130 delete_node<true>(dummy_head);
3131 assert(dummy_head ==
nullptr);
3135 get_iterator(const_iterator it)
3138 const_cast<typename iterator::node_ptr>(it.node));
3145 int64_t last_run_size = 0;
3148 for (
auto &tls_entry : tls_data) {
3150 auto &size_diff = tls_entry.size_diff;
3162 if (tls_entry.insert_stage == in_progress) {
3163 complete_insert(tls_entry);
3166 --(tls_entry.size_diff);
3173 assert(node ==
nullptr);
3175 last_run_size += size_diff;
3179 assert(last_run_size >= 0 ||
3181 static_cast<size_type>(std::abs(last_run_size)));
3187 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
3188 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
3193 complete_insert(tls_entry_type &tls_entry)
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();
3208 for (size_type level = 0; level < height; ++level) {
3209 assert(prev_nodes[level]->height() > level);
3210 assert(prev_nodes[level]->next(level) ==
3213 if (prev_nodes[level]->next(level) != node) {
3216 assert(n->next(level) == next_nodes[level]);
3217 prev_nodes[level]->set_next(pop, level, node);
3222 pop.
persist(&node,
sizeof(node));
3225 struct not_greater_compare {
3226 const key_compare &my_less_compare;
3228 not_greater_compare(
const key_compare &less_compare)
3229 : my_less_compare(less_compare)
3233 template <
typename K1,
typename K2>
3235 operator()(
const K1 &first,
const K2 &second)
const
3237 return !my_less_compare(second, first);
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;
3247 enumerable_thread_specific<tls_entry_type> tls_data;
3249 std::atomic<size_type> _size;
3259 template <
typename Key,
typename Value,
typename KeyCompare,
3260 typename RND_GENERATOR,
typename Allocator,
bool AllowMultimapping,
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;
3280 constexpr
static bool allow_multimapping = AllowMultimapping;
3282 static const key_type &
3283 get_key(const_reference val)
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
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
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