9 #ifndef PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
10 #define PMEMOBJ_CONCURRENT_SKIP_LIST_IMPL_HPP
19 #include <type_traits>
52 try_insert_node_finish_marker()
61 template <
typename MyAlloc,
typename OtherAlloc>
66 my_allocator = other_allocator;
73 template <
typename MyAlloc,
typename OtherAlloc>
83 template <
typename MyAlloc,
typename OtherAlloc>
88 my_allocator = std::move(other_allocator);
95 template <
typename MyAlloc,
typename OtherAlloc>
105 template <
typename MyAlloc,
typename OtherAlloc>
110 std::swap(my_allocator, other_allocator);
117 template <
typename MyAlloc,
typename OtherAlloc>
124 typename LockType = std::unique_lock<Mutex>>
125 class skip_list_node {
127 using value_type = Value;
128 using size_type = std::size_t;
129 using reference = value_type &;
130 using const_reference =
const value_type &;
131 using pointer = value_type *;
132 using const_pointer =
const value_type *;
135 using atomic_node_pointer = std::atomic<node_pointer>;
136 using mutex_type = Mutex;
137 using lock_type = LockType;
139 skip_list_node(size_type levels) : height_(levels)
141 for (size_type lev = 0; lev < height_; ++lev)
142 detail::create<atomic_node_pointer>(&get_next(lev),
145 assert(height() == levels);
146 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
151 for (size_type lev = 0; lev < height_; ++lev) {
152 VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
153 sizeof(get_next(lev)));
158 skip_list_node(size_type levels,
const node_pointer *new_nexts)
161 for (size_type lev = 0; lev < height_; ++lev)
162 detail::create<atomic_node_pointer>(&get_next(lev),
165 assert(height() == levels);
166 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
171 for (size_type lev = 0; lev < height_; ++lev) {
172 VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
173 sizeof(get_next(lev)));
180 for (size_type lev = 0; lev < height_; ++lev)
181 detail::destroy<atomic_node_pointer>(get_next(lev));
184 skip_list_node(
const skip_list_node &) =
delete;
186 skip_list_node &operator=(
const skip_list_node &) =
delete;
207 next(size_type level)
const
209 assert(level < height());
210 return get_next(level).load(std::memory_order_acquire);
218 set_next_tx(size_type level, node_pointer next)
220 assert(level < height());
221 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
222 auto &node = get_next(level);
223 obj::flat_transaction::snapshot<atomic_node_pointer>(&node);
224 node.store(next, std::memory_order_release);
228 set_next(obj::pool_base pop, size_type level, node_pointer next)
230 assert(level < height());
231 auto &node = get_next(level);
232 node.store(next, std::memory_order_release);
233 pop.persist(&node,
sizeof(node));
237 set_nexts(
const node_pointer *new_nexts, size_type h)
239 assert(h == height());
240 auto *nexts = get_nexts();
242 for (size_type i = 0; i < h; i++) {
243 nexts[i].store(new_nexts[i], std::memory_order_relaxed);
248 set_nexts(obj::pool_base pop,
const node_pointer *new_nexts,
251 set_nexts(new_nexts, h);
253 auto *nexts = get_nexts();
254 pop.persist(nexts,
sizeof(nexts[0]) * h);
267 return lock_type(mutex);
271 atomic_node_pointer *
274 return reinterpret_cast<atomic_node_pointer *
>(
this + 1);
277 atomic_node_pointer &
278 get_next(size_type level)
280 auto *arr = get_nexts();
284 const atomic_node_pointer &
285 get_next(size_type level)
const
288 reinterpret_cast<const atomic_node_pointer *
>(
this + 1);
299 template <
typename NodeType,
bool is_const>
300 class skip_list_iterator {
301 using node_type = NodeType;
302 using node_ptr =
typename std::conditional<is_const,
const node_type *,
304 friend class skip_list_iterator<node_type, true>;
307 using value_type =
typename node_type::value_type;
308 using iterator_category = std::forward_iterator_tag;
309 using difference_type = std::ptrdiff_t;
311 typename std::conditional<is_const,
312 typename node_type::const_reference,
313 typename node_type::reference>::type;
314 using pointer =
typename std::conditional<is_const,
const value_type *,
317 skip_list_iterator() : node(nullptr)
322 skip_list_iterator(
const skip_list_iterator &other) : node(other.node)
327 template <
typename U = void,
328 typename =
typename std::enable_if<is_const, U>::type>
329 skip_list_iterator(
const skip_list_iterator<node_type, false> &other)
334 reference operator*()
const
336 return *(node->get());
339 pointer operator->()
const
347 assert(node !=
nullptr);
348 node = node->next(0).get();
355 skip_list_iterator tmp = *
this;
361 operator=(
const skip_list_iterator &other)
368 explicit skip_list_iterator(node_type *n) : node(n)
372 template <
typename T = void,
373 typename =
typename std::enable_if<is_const, T>::type>
374 explicit skip_list_iterator(
const node_type *n) : node(n)
380 template <
typename Traits>
381 friend class concurrent_skip_list;
383 template <
typename T,
bool M,
bool U>
384 friend bool operator==(
const skip_list_iterator<T, M> &lhs,
385 const skip_list_iterator<T, U> &rhs);
387 template <
typename T,
bool M,
bool U>
388 friend bool operator!=(
const skip_list_iterator<T, M> &lhs,
389 const skip_list_iterator<T, U> &rhs);
392 template <
typename T,
bool M,
bool U>
394 operator==(
const skip_list_iterator<T, M> &lhs,
395 const skip_list_iterator<T, U> &rhs)
397 return lhs.node == rhs.node;
400 template <
typename T,
bool M,
bool U>
402 operator!=(
const skip_list_iterator<T, M> &lhs,
403 const skip_list_iterator<T, U> &rhs)
405 return lhs.node != rhs.node;
408 struct default_random_generator {
409 using gen_type = std::mt19937_64;
410 using result_type =
typename gen_type::result_type;
415 static thread_local gen_type engine(
416 static_cast<size_t>(time(0)));
421 static constexpr result_type
424 return gen_type::min();
427 static constexpr result_type
430 return gen_type::max();
434 template <
typename RndGenerator,
size_t MAX_LEVEL>
435 class geometric_level_generator {
437 using rnd_generator_type = RndGenerator;
439 static constexpr
size_t max_level = MAX_LEVEL;
446 static rnd_generator_type gen;
449 static thread_local std::geometric_distribution<size_t> d;
451 return (d(gen) % MAX_LEVEL) + 1;
483 template <
typename Traits>
486 using traits_type = Traits;
487 using key_type =
typename traits_type::key_type;
488 using mapped_type =
typename traits_type::mapped_type;
489 using value_type =
typename traits_type::value_type;
490 using size_type = std::size_t;
491 using difference_type = std::ptrdiff_t;
492 using key_compare =
typename traits_type::compare_type;
493 using allocator_type =
typename traits_type::allocator_type;
494 using allocator_traits_type = std::allocator_traits<allocator_type>;
496 using reference = value_type &;
497 using const_reference =
const value_type &;
498 using pointer =
typename allocator_traits_type::pointer;
499 using const_pointer =
typename allocator_traits_type::const_pointer;
501 using list_node_type = skip_list_node<value_type>;
503 using iterator = skip_list_iterator<list_node_type, false>;
504 using const_iterator = skip_list_iterator<list_node_type, true>;
506 static constexpr size_type MAX_LEVEL = traits_type::max_level;
508 using random_level_generator_type = geometric_level_generator<
509 typename traits_type::random_generator_type, MAX_LEVEL>;
510 using node_allocator_type =
typename std::allocator_traits<
511 allocator_type>::template rebind_alloc<uint8_t>;
512 using node_allocator_traits =
typename std::allocator_traits<
513 allocator_type>::template rebind_traits<uint8_t>;
514 using node_ptr = list_node_type *;
515 using const_node_ptr =
const list_node_type *;
519 using prev_array_type = std::array<node_ptr, MAX_LEVEL>;
520 using next_array_type = std::array<persistent_node_ptr, MAX_LEVEL>;
521 using node_lock_type =
typename list_node_type::lock_type;
522 using lock_array = std::array<node_lock_type, MAX_LEVEL>;
525 static constexpr
bool allow_multimapping =
526 traits_type::allow_multimapping;
538 check_tx_stage_work();
559 const key_compare &comp,
560 const allocator_type &alloc = allocator_type())
561 : _node_allocator(alloc), _compare(comp)
563 check_tx_stage_work();
590 template <
class InputIt>
592 const key_compare &comp = key_compare(),
593 const allocator_type &alloc = allocator_type())
594 : _node_allocator(alloc), _compare(comp)
596 check_tx_stage_work();
598 while (first != last)
599 internal_unsafe_emplace(*first++);
620 : _node_allocator(node_allocator_traits::
621 select_on_container_copy_construction(
622 other._node_allocator)),
623 _compare(other._compare),
624 _rnd_generator(other._rnd_generator)
626 check_tx_stage_work();
628 internal_copy(other);
629 assert(_size == other._size);
652 const allocator_type &alloc)
653 : _node_allocator(alloc),
654 _compare(other._compare),
655 _rnd_generator(other._rnd_generator)
657 check_tx_stage_work();
659 internal_copy(other);
660 assert(_size == other._size);
682 : _node_allocator(std::move(other._node_allocator)),
683 _compare(other._compare),
684 _rnd_generator(other._rnd_generator)
686 check_tx_stage_work();
688 internal_move(std::move(other));
711 const allocator_type &alloc)
712 : _node_allocator(alloc),
713 _compare(other._compare),
714 _rnd_generator(other._rnd_generator)
716 check_tx_stage_work();
718 if (alloc == other.get_allocator()) {
719 internal_move(std::move(other));
722 internal_copy(std::make_move_iterator(other.begin()),
723 std::make_move_iterator(other.end()));
738 assert(this->
size() ==
739 size_type(std::distance(this->
begin(), this->
end())));
757 if (dummy_head ==
nullptr)
760 auto pop = get_pool_base();
808 using pocca_t =
typename node_allocator_traits::
809 propagate_on_container_copy_assignment;
812 other._node_allocator,
814 _compare = other._compare;
815 _rnd_generator = other._rnd_generator;
817 internal_copy(other);
850 using pocma_t =
typename node_allocator_traits::
851 propagate_on_container_move_assignment;
853 if (pocma_t::value ||
854 _node_allocator == other._node_allocator) {
857 other._node_allocator,
859 _compare = other._compare;
860 _rnd_generator = other._rnd_generator;
861 internal_move(std::move(other));
864 std::make_move_iterator(other.begin()),
865 std::make_move_iterator(other.end()));
888 for (
auto it = il.begin(); it != il.end(); ++it)
889 internal_unsafe_emplace(*it);
910 std::pair<iterator, bool>
913 return internal_insert(value.first, value);
934 template <
typename P,
935 typename std::enable_if<
936 std::is_constructible<value_type, P &&>::value>::type>
937 std::pair<iterator, bool>
940 return emplace(std::forward<P>(value));
959 std::pair<iterator, bool>
962 return internal_insert(value.first, std::move(value));
983 insert(const_iterator hint, const_reference value)
986 return insert(value).first;
1009 template <
typename P,
1010 typename std::enable_if<
1011 std::is_constructible<value_type, P &&>::value>::type>
1032 template <
typename InputIterator>
1034 insert(InputIterator first, InputIterator last)
1036 for (InputIterator it = first; it != last; ++it)
1054 insert(std::initializer_list<value_type> ilist)
1056 insert(ilist.begin(), ilist.end());
1086 template <
typename... Args>
1087 std::pair<iterator, bool>
1090 return internal_emplace(std::forward<Args>(args)...);
1123 template <
typename... Args>
1128 return emplace(std::forward<Args>(args)...).first;
1154 template <
typename... Args>
1155 std::pair<iterator, bool>
1158 return internal_try_emplace(k, std::forward<Args>(args)...);
1184 template <
typename... Args>
1185 std::pair<iterator, bool>
1188 return internal_try_emplace(std::move(k),
1189 std::forward<Args>(args)...);
1218 template <
typename K,
typename... Args>
1219 typename std::enable_if<
1220 has_is_transparent<key_compare>::value &&
1221 std::is_constructible<key_type, K &&>::value,
1222 std::pair<iterator, bool>>::type
1225 return internal_try_emplace(std::forward<K>(k),
1226 std::forward<Args>(args)...);
1249 auto &size_diff = tls_data.
local().size_diff;
1250 return internal_erase(pos, size_diff);
1294 auto &size_diff = tls_data.
local().size_diff;
1297 while (first != last) {
1298 first = internal_erase(first, size_diff);
1302 return get_iterator(first);
1320 std::pair<iterator, iterator> range =
equal_range(key);
1321 size_type sz =
static_cast<size_type
>(
1322 std::distance(range.first, range.second));
1347 typename =
typename std::enable_if<
1348 has_is_transparent<key_compare>::value &&
1349 !std::is_convertible<K, iterator>::value &&
1350 !std::is_convertible<K, const_iterator>::value,
1355 std::pair<iterator, iterator> range =
equal_range(key);
1356 size_type sz =
static_cast<size_type
>(
1357 std::distance(range.first, range.second));
1375 return internal_get_bound(key, _compare);
1391 return internal_get_bound(key, _compare);
1407 template <
typename K,
1408 typename =
typename std::enable_if<
1409 has_is_transparent<key_compare>::value, K>::type>
1413 return internal_get_bound(x, _compare);
1429 template <
typename K,
1430 typename =
typename std::enable_if<
1431 has_is_transparent<key_compare>::value, K>::type>
1435 return internal_get_bound(x, _compare);
1451 return internal_get_bound(key, _compare);
1467 return internal_get_bound(key, _compare);
1484 template <
typename K,
1485 typename =
typename std::enable_if<
1486 has_is_transparent<key_compare>::value, K>::type>
1490 return internal_get_bound(x, _compare);
1507 template <
typename K,
1508 typename =
typename std::enable_if<
1509 has_is_transparent<key_compare>::value, K>::type>
1513 return internal_get_bound(x, _compare);
1529 return internal_get_bound(key, not_greater_compare(_compare));
1545 return internal_get_bound(key, not_greater_compare(_compare));
1561 template <
typename K,
1562 typename =
typename std::enable_if<
1563 has_is_transparent<key_compare>::value, K>::type>
1567 return internal_get_bound(x, not_greater_compare(_compare));
1583 template <
typename K,
1584 typename =
typename std::enable_if<
1585 has_is_transparent<key_compare>::value, K>::type>
1589 return internal_get_bound(x, not_greater_compare(_compare));
1605 return internal_get_bound(key, not_greater_compare(_compare));
1621 return internal_get_bound(key, not_greater_compare(_compare));
1637 template <
typename K,
1638 typename =
typename std::enable_if<
1639 has_is_transparent<key_compare>::value, K>::type>
1643 return internal_get_bound(x, not_greater_compare(_compare));
1659 template <
typename K,
1660 typename =
typename std::enable_if<
1661 has_is_transparent<key_compare>::value, K>::type>
1665 return internal_get_bound(x, not_greater_compare(_compare));
1681 auto it = internal_get_biggest_less_than(key, _compare);
1683 const_cast<typename iterator::node_ptr
>(it.node));
1699 return internal_get_biggest_less_than(key, _compare);
1715 template <
typename K,
1716 typename =
typename std::enable_if<
1717 has_is_transparent<key_compare>::value, K>::type>
1721 auto it = internal_get_biggest_less_than(key, _compare);
1723 const_cast<typename iterator::node_ptr
>(it.node));
1739 template <
typename K,
1740 typename =
typename std::enable_if<
1741 has_is_transparent<key_compare>::value, K>::type>
1745 return internal_get_biggest_less_than(key, _compare);
1761 auto it = internal_get_biggest_less_than(
1762 key, not_greater_compare(_compare));
1764 const_cast<typename iterator::node_ptr
>(it.node));
1780 return internal_get_biggest_less_than(
1781 key, not_greater_compare(_compare));
1797 template <
typename K,
1798 typename =
typename std::enable_if<
1799 has_is_transparent<key_compare>::value, K>::type>
1803 auto it = internal_get_biggest_less_than(
1804 key, not_greater_compare(_compare));
1806 const_cast<typename iterator::node_ptr
>(it.node));
1822 template <
typename K,
1823 typename =
typename std::enable_if<
1824 has_is_transparent<key_compare>::value, K>::type>
1828 return internal_get_biggest_less_than(
1829 key, not_greater_compare(_compare));
1843 return internal_find(key);
1857 return internal_find(key);
1872 template <
typename K,
1873 typename =
typename std::enable_if<
1874 has_is_transparent<key_compare>::value, K>::type>
1878 return internal_find(x);
1893 template <
typename K,
1894 typename =
typename std::enable_if<
1895 has_is_transparent<key_compare>::value, K>::type>
1899 return internal_find(x);
1914 return internal_count(key);
1929 template <
typename K,
1930 typename =
typename std::enable_if<
1931 has_is_transparent<key_compare>::value, K>::type>
1935 return internal_count(key);
1964 template <
typename K,
1965 typename =
typename std::enable_if<
1966 has_is_transparent<key_compare>::value, K>::type>
1984 assert(dummy_head->height() > 0);
1991 assert(current->height() > 0);
1993 delete_node(current);
1997 node_ptr head = dummy_head.
get();
1998 for (size_type i = 0; i < head->height(); ++i) {
1999 head->set_next_tx(i,
nullptr);
2018 return iterator(dummy_head.
get()->next(0).get());
2030 return const_iterator(dummy_head.
get()->next(0).get());
2042 return const_iterator(dummy_head.
get()->next(0).get());
2055 return iterator(
nullptr);
2068 return const_iterator(
nullptr);
2081 return const_iterator(
nullptr);
2093 return _size.load(std::memory_order_relaxed);
2106 return std::numeric_limits<difference_type>::max();
2138 using pocs_t =
typename node_allocator_traits::
2139 propagate_on_container_swap;
2142 std::swap(_compare, other._compare);
2143 std::swap(_rnd_generator, other._rnd_generator);
2144 std::swap(dummy_head, other.dummy_head);
2145 on_init_size.
swap(other.on_init_size);
2149 (
size_t *)&(other._size));
2150 _size = other._size.exchange(_size,
2151 std::memory_order_relaxed);
2174 std::pair<iterator, iterator>
2177 return std::pair<iterator, iterator>(
lower_bound(key),
2200 std::pair<const_iterator, const_iterator>
2203 return std::pair<const_iterator, const_iterator>(
2229 template <
typename K,
2230 typename =
typename std::enable_if<
2231 has_is_transparent<key_compare>::value, K>::type>
2232 std::pair<iterator, iterator>
2235 return std::pair<iterator, iterator>(
lower_bound(x),
2261 template <
typename K,
2262 typename =
typename std::enable_if<
2263 has_is_transparent<key_compare>::value, K>::type>
2264 std::pair<const_iterator, const_iterator>
2267 return std::pair<const_iterator, const_iterator>(
2295 enum insert_stage_type : uint8_t { not_started = 0, in_progress = 1 };
2300 struct tls_entry_type {
2301 persistent_node_ptr ptr;
2305 char reserved[64 -
sizeof(decltype(ptr)) -
2306 sizeof(decltype(size_diff)) -
2307 sizeof(decltype(insert_stage))];
2309 static_assert(
sizeof(tls_entry_type) == 64,
2310 "The size of tls_entry_type should be 64 bytes.");
2320 check_tx_stage_work()
const
2322 if (pmemobj_tx_stage() != TX_STAGE_WORK)
2324 "Function called out of transaction scope.");
2331 if (pmemobj_tx_stage() != TX_STAGE_NONE)
2333 "Function called inside transaction scope.");
2344 create_dummy_head();
2350 assert(this->
empty());
2351 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2352 dummy_head = other.dummy_head;
2353 other.dummy_head =
nullptr;
2354 other.create_dummy_head();
2356 _size.store(other._size.load(std::memory_order_relaxed),
2357 std::memory_order_relaxed);
2358 on_init_size = other.on_init_size;
2361 static const_reference
2362 get_val(const_node_ptr n)
2375 static const key_type &
2376 get_key(const_node_ptr n)
2379 return traits_type::get_key(get_val(n));
2382 template <
typename K>
2384 internal_find(
const K &key)
2387 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2392 template <
typename K>
2394 internal_find(
const K &key)
const
2397 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2402 template <
typename K>
2404 internal_count(
const K &key)
const
2406 if (allow_multimapping) {
2407 std::pair<const_iterator, const_iterator> range =
2409 return static_cast<size_type
>(
2410 std::distance(range.first, range.second));
2412 return (
find(key) ==
end()) ? size_type(0) : size_type(1);
2425 template <
typename K,
typename po
inter_type,
typename comparator>
2427 internal_find_position(size_type level, pointer_type &prev,
2428 const K &key,
const comparator &cmp)
const
2430 assert(level < prev->height());
2431 persistent_node_ptr next = prev->next(level);
2432 pointer_type curr = next.
get();
2434 while (curr && cmp(get_key(curr), key)) {
2436 assert(level < prev->height());
2437 next = prev->next(level);
2454 template <
typename K>
2456 find_insert_pos(prev_array_type &prev_nodes,
2457 next_array_type &next_nodes,
const K &key)
2459 if (allow_multimapping) {
2460 fill_prev_next_arrays(prev_nodes, next_nodes, key,
2461 not_greater_compare(_compare));
2463 fill_prev_next_arrays(prev_nodes, next_nodes, key,
2479 template <
typename K,
typename comparator>
2481 fill_prev_next_arrays(prev_array_type &prev_nodes,
2482 next_array_type &next_nodes,
const K &key,
2483 const comparator &cmp)
2485 node_ptr prev = dummy_head.
get();
2486 prev_nodes.fill(prev);
2487 next_nodes.fill(
nullptr);
2489 for (size_type h = prev->height(); h > 0; --h) {
2490 persistent_node_ptr next =
2491 internal_find_position(h - 1, prev, key, cmp);
2492 prev_nodes[h - 1] = prev;
2493 next_nodes[h - 1] = next;
2497 template <
typename K,
typename... Args>
2498 std::pair<iterator, bool>
2499 internal_try_emplace(K &&key, Args &&... args)
2501 return internal_insert(
2502 key, std::piecewise_construct,
2503 std::forward_as_tuple(std::forward<K>(key)),
2504 std::forward_as_tuple(std::forward<Args>(args)...));
2507 template <
typename... Args>
2508 std::pair<iterator, bool>
2509 internal_emplace(Args &&... args)
2512 tls_entry_type &tls_entry = tls_data.
local();
2513 obj::pool_base pop = get_pool_base();
2516 assert(tls_entry.ptr ==
nullptr);
2518 create_node(std::forward<Args>(args)...);
2519 ++tls_entry.size_diff;
2520 tls_entry.insert_stage = not_started;
2523 node_ptr n = tls_entry.ptr.get();
2524 size_type height = n->height();
2526 std::pair<iterator, bool> insert_result = internal_insert_node(
2528 [&](
const next_array_type &next_nodes)
2529 -> persistent_node_ptr & {
2530 assert(tls_entry.insert_stage == not_started);
2531 assert(tls_entry.ptr !=
nullptr);
2533 n->set_nexts(pop, next_nodes.data(), height);
2535 tls_entry.insert_stage = in_progress;
2536 pop.persist(&(tls_entry.insert_stage),
2537 sizeof(tls_entry.insert_stage));
2539 return tls_entry.ptr;
2542 if (!insert_result.second) {
2543 assert(tls_entry.ptr !=
nullptr);
2544 assert(tls_entry.insert_stage == not_started);
2547 --tls_entry.size_diff;
2548 delete_node(tls_entry.ptr);
2549 tls_entry.ptr =
nullptr;
2553 assert(tls_entry.ptr ==
nullptr);
2554 return insert_result;
2561 template <
typename... Args>
2562 std::pair<iterator, bool>
2563 internal_unsafe_emplace(Args &&... args)
2565 check_tx_stage_work();
2567 persistent_node_ptr new_node =
2568 create_node(std::forward<Args>(args)...);
2570 node_ptr n = new_node.get();
2571 size_type height = n->height();
2573 std::pair<iterator, bool> insert_result = internal_insert_node(
2575 [&](
const next_array_type &next_nodes)
2576 -> persistent_node_ptr & {
2577 assert(new_node !=
nullptr);
2579 n->set_nexts(next_nodes.data(), height);
2584 if (insert_result.second) {
2587 assert(new_node !=
nullptr);
2589 delete_node(new_node);
2592 return insert_result;
2598 template <
typename K,
typename... Args>
2599 std::pair<iterator, bool>
2600 internal_insert(
const K &key, Args &&... args)
2603 tls_entry_type &tls_entry = tls_data.
local();
2604 assert(tls_entry.ptr ==
nullptr);
2606 size_type height = random_level();
2608 std::pair<iterator, bool> insert_result = internal_insert_node(
2610 [&](
const next_array_type &next_nodes)
2611 -> persistent_node_ptr & {
2612 obj::pool_base pop = get_pool_base();
2615 tls_entry.ptr = create_node(
2616 std::forward_as_tuple(
2617 height, next_nodes.data()),
2618 std::forward_as_tuple(
2619 std::forward<Args>(args)...));
2621 ++(tls_entry.size_diff);
2622 tls_entry.insert_stage = in_progress;
2625 assert(tls_entry.ptr !=
nullptr);
2626 return tls_entry.ptr;
2629 assert(tls_entry.ptr ==
nullptr);
2631 return insert_result;
2637 template <
typename K,
typename PrepareNode>
2638 std::pair<iterator, bool>
2639 internal_insert_node(
const K &key, size_type height,
2640 PrepareNode &&prepare_new_node)
2642 prev_array_type prev_nodes;
2643 next_array_type next_nodes;
2644 node_ptr n =
nullptr;
2647 find_insert_pos(prev_nodes, next_nodes, key);
2649 node_ptr next = next_nodes[0].get();
2650 if (next && !allow_multimapping &&
2651 !_compare(key, get_key(next))) {
2653 return std::pair<iterator, bool>(iterator(next),
2657 }
while ((n = try_insert_node(prev_nodes, next_nodes, height,
2658 std::forward<PrepareNode>(
2659 prepare_new_node))) ==
2663 return std::pair<iterator, bool>(iterator(n),
true);
2671 template <
typename PrepareNode>
2673 try_insert_node(prev_array_type &prev_nodes,
2674 const next_array_type &next_nodes, size_type height,
2675 PrepareNode &&prepare_new_node)
2677 assert(dummy_head->height() >= height);
2680 if (!try_lock_nodes(height, prev_nodes, next_nodes, locks)) {
2684 node_lock_type new_node_lock;
2686 persistent_node_ptr &new_node = prepare_new_node(next_nodes);
2687 assert(new_node !=
nullptr);
2688 node_ptr n = new_node.get();
2696 new_node_lock = n->acquire();
2698 obj::pool_base pop = get_pool_base();
2708 for (size_type level = 0; level < height; ++level) {
2709 assert(prev_nodes[level]->height() > level);
2710 assert(prev_nodes[level]->next(level) ==
2712 assert(prev_nodes[level]->next(level) ==
2714 prev_nodes[level]->set_next(pop, level, new_node);
2718 try_insert_node_finish_marker();
2725 pop.persist(&new_node,
sizeof(new_node));
2728 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2729 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
2741 check_prev_array(
const prev_array_type &prevs, size_type height)
2743 for (size_type l = 1; l < height; ++l) {
2744 if (prevs[l] == dummy_head.
get()) {
2748 assert(prevs[l - 1] != dummy_head.
get());
2749 assert(!_compare(get_key(prevs[l - 1]),
2750 get_key(prevs[l])));
2757 try_lock_nodes(size_type height, prev_array_type &prevs,
2758 const next_array_type &nexts, lock_array &locks)
2760 assert(check_prev_array(prevs, height));
2762 for (size_type l = 0; l < height; ++l) {
2763 if (l == 0 || prevs[l] != prevs[l - 1]) {
2764 locks[l] = prevs[l]->acquire();
2767 persistent_node_ptr next = prevs[l]->next(l);
2768 if (next != nexts[l])
2789 template <
typename K,
typename comparator>
2791 internal_get_bound(
const K &key,
const comparator &cmp)
const
2793 const_node_ptr prev = dummy_head.
get();
2794 assert(prev->height() > 0);
2795 persistent_node_ptr next =
nullptr;
2797 for (size_type h = prev->height(); h > 0; --h) {
2798 next = internal_find_position(h - 1, prev, key, cmp);
2801 return const_iterator(next.get());
2815 template <
typename K,
typename comparator>
2817 internal_get_bound(
const K &key,
const comparator &cmp)
2819 node_ptr prev = dummy_head.
get();
2820 assert(prev->height() > 0);
2821 persistent_node_ptr next =
nullptr;
2823 for (size_type h = prev->height(); h > 0; --h) {
2824 next = internal_find_position(h - 1, prev, key, cmp);
2827 return iterator(next.get());
2841 template <
typename K,
typename comparator>
2843 internal_get_biggest_less_than(
const K &key,
2844 const comparator &cmp)
const
2846 const_node_ptr prev = dummy_head.
get();
2847 assert(prev->height() > 0);
2849 for (size_type h = prev->height(); h > 0; --h) {
2850 internal_find_position(h - 1, prev, key, cmp);
2853 if (prev == dummy_head.
get())
2856 return const_iterator(prev);
2860 internal_erase(const_iterator pos, obj::p<difference_type> &size_diff)
2862 assert(pos !=
end());
2864 obj::pool_base pop = get_pool_base();
2866 std::pair<persistent_node_ptr, persistent_node_ptr>
2867 extract_result(
nullptr,
nullptr);
2870 extract_result = internal_extract(pos);
2873 assert(extract_result.first !=
nullptr);
2874 delete_node(extract_result.first);
2880 return iterator(extract_result.second.get());
2886 std::pair<persistent_node_ptr, persistent_node_ptr>
2887 internal_extract(const_iterator it)
2889 assert(dummy_head->height() > 0);
2890 assert(it !=
end());
2891 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2893 const key_type &key = traits_type::get_key(*it);
2895 prev_array_type prev_nodes;
2896 next_array_type next_nodes;
2898 fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
2900 node_ptr erase_node = next_nodes[0].get();
2901 assert(erase_node !=
nullptr);
2903 if (!_compare(key, get_key(erase_node))) {
2907 assert(erase_node == it.node);
2908 return internal_extract_node(prev_nodes, next_nodes,
2912 return std::pair<persistent_node_ptr, persistent_node_ptr>(
2916 std::pair<persistent_node_ptr, persistent_node_ptr>
2917 internal_extract_node(
const prev_array_type &prev_nodes,
2918 const next_array_type &next_nodes,
2919 node_ptr erase_node)
2921 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2922 assert(erase_node !=
nullptr);
2923 for (size_type level = 0; level < erase_node->height();
2925 assert(prev_nodes[level]->height() > level);
2926 assert(next_nodes[level].get() == erase_node);
2927 prev_nodes[level]->set_next_tx(level,
2928 erase_node->next(level));
2931 return std::pair<persistent_node_ptr, persistent_node_ptr>(
2932 next_nodes[0], erase_node->next(0));
2940 get_pool_base()
const
2942 PMEMobjpool *pop = pmemobj_pool_by_ptr(
this);
2943 return obj::pool_base(pop);
2949 internal_copy(other.begin(), other.end());
2952 template <
typename Iterator>
2954 internal_copy(Iterator first, Iterator last)
2956 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2958 prev_array_type prev_nodes;
2959 prev_nodes.fill(dummy_head.
get());
2962 for (; first != last; ++first, ++sz) {
2963 persistent_node_ptr new_node = create_node(*first);
2964 node_ptr n = new_node.get();
2965 for (size_type level = 0; level < n->height();
2967 prev_nodes[level]->set_next_tx(level, new_node);
2968 prev_nodes[level] = n;
2980 assert(std::is_sorted(
2982 [&](
const value_type &lhs,
const value_type &rhs) {
2983 return lhs.first < rhs.first;
2991 return _rnd_generator();
2995 calc_node_size(size_type height)
2997 return sizeof(list_node_type) +
2998 height *
sizeof(
typename list_node_type::node_pointer);
3002 template <
typename... Args>
3004 create_node(Args &&... args)
3006 size_type levels = random_level();
3009 std::forward_as_tuple(levels),
3010 std::forward_as_tuple(std::forward<Args>(args)...));
3013 template <
typename... NodeArgs,
typename... ValueArgs>
3015 create_node(std::tuple<NodeArgs...> &&node_args,
3016 std::tuple<ValueArgs...> &&value_args)
3018 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3020 persistent_node_ptr node = creates_dummy_node(
3021 std::forward<std::tuple<NodeArgs...>>(node_args),
3022 index_sequence_for<NodeArgs...>{});
3024 construct_value_type(
3026 std::forward<std::tuple<ValueArgs...>>(value_args),
3027 index_sequence_for<ValueArgs...>{});
3032 template <
typename Tuple,
size_t... I>
3034 construct_value_type(persistent_node_ptr node, Tuple &&args,
3035 index_sequence<I...>)
3037 node_ptr new_node = node.get();
3039 node_allocator_traits::construct(
3040 _node_allocator, new_node->get(),
3041 std::get<I>(std::forward<Tuple>(args))...);
3052 dummy_head = creates_dummy_node(MAX_LEVEL);
3055 template <
typename Tuple,
size_t... I>
3057 creates_dummy_node(Tuple &&args, index_sequence<I...>)
3059 return creates_dummy_node(
3060 std::get<I>(std::forward<Tuple>(args))...);
3072 template <
typename... Args>
3074 creates_dummy_node(size_type height, Args &&... args)
3076 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3077 size_type sz = calc_node_size(height);
3079 persistent_node_ptr n =
3080 node_allocator_traits::allocate(_node_allocator, sz)
3083 assert(n !=
nullptr);
3085 node_allocator_traits::construct(_node_allocator, n.get(),
3087 std::forward<Args>(args)...);
3092 template <
bool is_dummy = false>
3094 delete_node(persistent_node_ptr &node)
3096 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
3097 node_ptr n = node.get();
3098 size_type sz = calc_node_size(n->height());
3102 node_allocator_traits::destroy(_node_allocator,
3105 node_allocator_traits::destroy(_node_allocator, n);
3107 deallocate_node(node, sz);
3112 deallocate_node(persistent_node_ptr &node, size_type sz)
3120 obj::persistent_ptr<uint8_t> tmp =
3121 node.to_persistent_ptr().raw();
3122 node_allocator_traits::deallocate(_node_allocator, tmp, sz);
3128 assert(dummy_head !=
nullptr);
3129 delete_node<true>(dummy_head);
3130 assert(dummy_head ==
nullptr);
3134 get_iterator(const_iterator it)
3137 const_cast<typename iterator::node_ptr
>(it.node));
3144 int64_t last_run_size = 0;
3145 obj::pool_base pop = get_pool_base();
3147 for (
auto &tls_entry : tls_data) {
3148 persistent_node_ptr &node = tls_entry.ptr;
3149 auto &size_diff = tls_entry.size_diff;
3161 if (tls_entry.insert_stage == in_progress) {
3162 complete_insert(tls_entry);
3165 --(tls_entry.size_diff);
3172 assert(node ==
nullptr);
3174 last_run_size += size_diff;
3178 assert(last_run_size >= 0 ||
3180 static_cast<size_type
>(std::abs(last_run_size)));
3183 on_init_size +=
static_cast<size_t>(last_run_size);
3185 _size = on_init_size;
3186 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
3187 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
3192 complete_insert(tls_entry_type &tls_entry)
3194 persistent_node_ptr &node = tls_entry.ptr;
3195 assert(node !=
nullptr);
3196 assert(tls_entry.insert_stage == in_progress);
3197 prev_array_type prev_nodes;
3198 next_array_type next_nodes;
3199 node_ptr n = node.get();
3200 const key_type &key = get_key(n);
3201 size_type height = n->height();
3203 fill_prev_next_arrays(prev_nodes, next_nodes, key, _compare);
3204 obj::pool_base pop = get_pool_base();
3207 for (size_type level = 0; level < height; ++level) {
3208 assert(prev_nodes[level]->height() > level);
3209 assert(prev_nodes[level]->next(level) ==
3212 if (prev_nodes[level]->next(level) != node) {
3215 assert(n->next(level) == next_nodes[level]);
3216 prev_nodes[level]->set_next(pop, level, node);
3221 pop.persist(&node,
sizeof(node));
3224 struct not_greater_compare {
3225 const key_compare &my_less_compare;
3227 not_greater_compare(
const key_compare &less_compare)
3228 : my_less_compare(less_compare)
3232 template <
typename K1,
typename K2>
3234 operator()(
const K1 &first,
const K2 &second)
const
3236 return !my_less_compare(second, first);
3240 const uint64_t pool_uuid = pmemobj_oid(
this).pool_uuid_lo;
3241 node_allocator_type _node_allocator;
3242 key_compare _compare;
3243 random_level_generator_type _rnd_generator;
3244 persistent_node_ptr dummy_head;
3246 enumerable_thread_specific<tls_entry_type> tls_data;
3248 std::atomic<size_type> _size;
3255 obj::p<size_type> on_init_size;
3258 template <
typename Key,
typename Value,
typename KeyCompare,
3259 typename RND_GENERATOR,
typename Allocator,
bool AllowMultimapping,
3263 static constexpr
size_t max_level = MAX_LEVEL;
3264 using random_generator_type = RND_GENERATOR;
3265 using key_type = Key;
3266 using mapped_type = Value;
3267 using compare_type = KeyCompare;
3268 using value_type = pair<const key_type, mapped_type>;
3269 using reference = value_type &;
3270 using const_reference =
const value_type &;
3271 using allocator_type = Allocator;
3279 constexpr
static bool allow_multimapping = AllowMultimapping;
3281 static const key_type &
3282 get_key(const_reference val)
Atomic specialization for self_relative_ptr.
Persistent memory aware implementation of the concurrent skip list.
Definition: concurrent_skip_list_impl.hpp:484
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:1947
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:1488
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:1389
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:1603
concurrent_skip_list & operator=(const concurrent_skip_list &other)
Copy assignment operator.
Definition: concurrent_skip_list_impl.hpp:801
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:1826
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:2201
std::pair< iterator, bool > insert(P &&value)
Inserts value.
Definition: concurrent_skip_list_impl.hpp:938
concurrent_skip_list(const concurrent_skip_list &other, const allocator_type &alloc)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:651
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:1449
void swap(concurrent_skip_list &other)
XXX: Implement get_allocator() interface.
Definition: concurrent_skip_list_impl.hpp:2134
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:1743
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:1897
~concurrent_skip_list()
Destructor.
Definition: concurrent_skip_list_impl.hpp:776
const_iterator cend() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2079
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:1088
concurrent_skip_list()
Default constructor.
Definition: concurrent_skip_list_impl.hpp:536
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:1527
const_iterator cbegin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2040
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:1373
const_iterator begin() const
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2028
iterator begin()
Returns an iterator to the first element of the container.
Definition: concurrent_skip_list_impl.hpp:2016
const key_compare & key_comp() const
Returns a const reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2277
concurrent_skip_list(concurrent_skip_list &&other)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:681
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:2233
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:1801
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:2104
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:1679
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:1697
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:983
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:1223
concurrent_skip_list(const key_compare &comp, const allocator_type &alloc=allocator_type())
Constructs an empty container.
Definition: concurrent_skip_list_impl.hpp:558
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:1465
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:1619
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:1013
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:1933
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:2175
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:1353
concurrent_skip_list(const concurrent_skip_list &other)
Copy constructor.
Definition: concurrent_skip_list_impl.hpp:619
key_compare & key_comp()
Returns a reference to the object that compares the keys.
Definition: concurrent_skip_list_impl.hpp:2288
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:2265
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:1968
iterator end()
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2053
iterator find(const key_type &key)
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1841
const_iterator find(const key_type &key) const
Finds an element with key equivalent to key.
Definition: concurrent_skip_list_impl.hpp:1855
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:1411
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:883
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:591
std::pair< iterator, bool > insert(const value_type &value)
Inserts value in a thread-safe way.
Definition: concurrent_skip_list_impl.hpp:911
iterator unsafe_erase(const_iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1270
iterator find(const K &x)
Finds an element with key that compares equivalent to the value x.
Definition: concurrent_skip_list_impl.hpp:1876
const_iterator end() const
Returns an iterator to the element following the last element of the map.
Definition: concurrent_skip_list_impl.hpp:2066
void clear()
Erases all elements from the container transactionally.
Definition: concurrent_skip_list_impl.hpp:1982
concurrent_skip_list(concurrent_skip_list &&other, const allocator_type &alloc)
Move constructor.
Definition: concurrent_skip_list_impl.hpp:710
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:1778
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:1511
concurrent_skip_list & operator=(concurrent_skip_list &&other)
Move assignment operator.
Definition: concurrent_skip_list_impl.hpp:843
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:1318
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:1125
void insert(InputIterator first, InputIterator last)
Inserts elements from range [first, last).
Definition: concurrent_skip_list_impl.hpp:1034
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:1565
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:1759
void runtime_initialize()
Initialize concurrent_skip_list after process restart.
Definition: concurrent_skip_list_impl.hpp:734
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:1912
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:1433
iterator unsafe_erase(iterator pos)
Removes the element at pos from the container.
Definition: concurrent_skip_list_impl.hpp:1246
void free_data()
Should be called before concurrent_skip_list destructor is called.
Definition: concurrent_skip_list_impl.hpp:755
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:1156
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:1587
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:1186
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:1663
bool empty() const
Checks if the container has no elements, i.e.
Definition: concurrent_skip_list_impl.hpp:2116
size_type size() const
Returns the number of elements in the container, i.e.
Definition: concurrent_skip_list_impl.hpp:2091
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:1543
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:1719
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:1641
void insert(std::initializer_list< value_type > ilist)
Inserts elements from initializer list ilist.
Definition: concurrent_skip_list_impl.hpp:1054
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:1290
std::pair< iterator, bool > insert(value_type &&value)
Inserts value using move semantic.
Definition: concurrent_skip_list_impl.hpp:960
void clear()
Removes all elements from the container.
Definition: enumerable_thread_specific.hpp:345
reference local()
Returns data reference for the current thread.
Definition: enumerable_thread_specific.hpp:305
static void commit()
Manually commit a transaction.
Definition: transaction.hpp:330
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:437
Persistent self-relative pointer class.
Definition: self_relative_ptr.hpp:82
element_type * get() const noexcept
Get the direct pointer.
Definition: self_relative_ptr.hpp:197
typename detail::transaction_base< true >::manual manual
C++ manual scope transaction class.
Definition: transaction.hpp:745
static void run(obj::pool_base &pool, std::function< void()> tx, Locks &... locks)
Execute a closure-like transaction and lock locks.
Definition: transaction.hpp:810
Persistent memory resident mutex implementation.
Definition: mutex.hpp:33
void swap(p &other)
Swaps two p objects of the same type.
Definition: p.hpp:141
The non-template pool base class.
Definition: pool.hpp:51
Custom pool error class.
Definition: pexceptions.hpp:84
Custom transaction error class.
Definition: pexceptions.hpp:167
Commonly used functionality.
A persistent version of thread-local storage.
Functions for lifetime management.
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:85
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:63
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:107
Persistent memory namespace.
Definition: allocation_flag.hpp:15
Persistent smart pointer.
Persistent self-relative smart pointer.
Commonly used SFINAE helpers.
C++ pmemobj transactions.