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>
20 #include <libpmemobj++/detail/persistent_pool_ptr.hpp>
45 try_insert_node_finish_marker()
52 store_with_release(persistent_pool_ptr<T> &dst, persistent_pool_ptr<T> src)
54 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
55 ANNOTATE_HAPPENS_BEFORE(&dst);
57 std::atomic_thread_fence(std::memory_order_release);
63 inline persistent_pool_ptr<T>
64 load_with_acquire(
const persistent_pool_ptr<T> &ptr)
66 persistent_pool_ptr<T> ret = ptr;
67 std::atomic_thread_fence(std::memory_order_acquire);
68 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
69 ANNOTATE_HAPPENS_AFTER(&ptr);
74 template <
typename Compare>
75 using is_transparent =
typename Compare::is_transparent;
77 template <
typename Compare>
78 using has_is_transparent = detail::supports<Compare, is_transparent>;
84 template <
typename MyAlloc,
typename OtherAlloc>
89 my_allocator = other_allocator;
96 template <
typename MyAlloc,
typename OtherAlloc>
106 template <
typename MyAlloc,
typename OtherAlloc>
111 my_allocator = std::move(other_allocator);
118 template <
typename MyAlloc,
typename OtherAlloc>
128 template <
typename MyAlloc,
typename OtherAlloc>
133 std::swap(my_allocator, other_allocator);
140 template <
typename MyAlloc,
typename OtherAlloc>
147 typename LockType = std::unique_lock<Mutex>>
148 class skip_list_node {
150 using value_type = Value;
151 using size_type = std::size_t;
152 using reference = value_type &;
153 using const_reference =
const value_type &;
154 using pointer = value_type *;
155 using const_pointer =
const value_type *;
156 using node_pointer = persistent_pool_ptr<skip_list_node>;
157 using mutex_type = Mutex;
158 using lock_type = LockType;
160 skip_list_node(size_type levels) : height_(levels)
162 for (size_type lev = 0; lev < height_; ++lev)
163 detail::create<node_pointer>(&get_next(lev),
nullptr);
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(node_pointer));
178 skip_list_node(size_type levels,
const node_pointer *new_nexts)
181 for (size_type lev = 0; lev < height_; ++lev)
182 detail::create<node_pointer>(&get_next(lev),
185 assert(height() == levels);
186 #if LIBPMEMOBJ_CPP_VG_HELGRIND_ENABLED
191 for (size_type lev = 0; lev < height_; ++lev) {
192 VALGRIND_HG_DISABLE_CHECKING(&get_next(lev),
193 sizeof(node_pointer));
200 for (size_type lev = 0; lev < height_; ++lev)
201 detail::destroy<node_pointer>(get_next(lev));
204 skip_list_node(
const skip_list_node &) =
delete;
206 skip_list_node &operator=(
const skip_list_node &) =
delete;
227 next(size_type level)
const
229 assert(level < height());
230 return load_with_acquire(get_next(level));
234 set_next(size_type level, node_pointer next)
236 assert(level < height());
237 store_with_release(get_next(level), next);
241 set_next(obj::pool_base pop, size_type level, node_pointer next)
243 set_next(level, next);
244 pop.persist(&get_next(level),
sizeof(node_pointer));
248 set_nexts(
const node_pointer *new_nexts, size_type h)
250 assert(h == height());
251 node_pointer *nexts = get_nexts();
253 std::copy(new_nexts, new_nexts + h, nexts);
257 set_nexts(obj::pool_base pop,
const node_pointer *new_nexts,
260 set_nexts(new_nexts, h);
262 node_pointer *nexts = get_nexts();
263 pop.persist(nexts,
sizeof(node_pointer) * h);
276 return lock_type(mutex);
283 return reinterpret_cast<node_pointer *
>(
this + 1);
287 get_next(size_type level)
289 node_pointer *arr = get_nexts();
294 get_next(size_type level)
const
296 const node_pointer *arr =
297 reinterpret_cast<const node_pointer *
>(
this + 1);
308 template <
typename NodeType,
bool is_const>
309 class skip_list_iterator {
310 using node_type = NodeType;
311 using node_ptr =
typename std::conditional<is_const,
const node_type *,
313 friend class skip_list_iterator<node_type, true>;
316 using value_type =
typename node_type::value_type;
317 using iterator_category = std::forward_iterator_tag;
318 using difference_type = std::ptrdiff_t;
320 typename std::conditional<is_const,
321 typename node_type::const_reference,
322 typename node_type::reference>::type;
323 using pointer =
typename std::conditional<is_const,
const value_type *,
326 skip_list_iterator() : pool_uuid(0), node(nullptr)
331 skip_list_iterator(
const skip_list_iterator &other)
332 : pool_uuid(other.pool_uuid), node(other.node)
337 template <
typename U = void,
338 typename =
typename std::enable_if<is_const, U>::type>
339 skip_list_iterator(
const skip_list_iterator<node_type, false> &other)
340 : pool_uuid(other.pool_uuid), node(other.node)
344 reference operator*()
const
346 return *(node->get());
349 pointer operator->()
const
357 assert(node !=
nullptr);
358 node = node->next(0).get(pool_uuid);
365 skip_list_iterator tmp = *
this;
371 skip_list_iterator(uint64_t pool_uuid, node_type *n)
372 : pool_uuid(pool_uuid), node(n)
376 template <
typename T = void,
377 typename =
typename std::enable_if<is_const, T>::type>
378 skip_list_iterator(uint64_t pool_uuid,
const node_type *n)
379 : pool_uuid(pool_uuid), node(n)
387 template <
typename Traits>
388 friend class concurrent_skip_list;
390 template <
typename T,
bool M,
bool U>
391 friend bool operator==(
const skip_list_iterator<T, M> &lhs,
392 const skip_list_iterator<T, U> &rhs);
394 template <
typename T,
bool M,
bool U>
395 friend bool operator!=(
const skip_list_iterator<T, M> &lhs,
396 const skip_list_iterator<T, U> &rhs);
399 template <
typename T,
bool M,
bool U>
401 operator==(
const skip_list_iterator<T, M> &lhs,
402 const skip_list_iterator<T, U> &rhs)
404 return lhs.node == rhs.node;
407 template <
typename T,
bool M,
bool U>
409 operator!=(
const skip_list_iterator<T, M> &lhs,
410 const skip_list_iterator<T, U> &rhs)
412 return lhs.node != rhs.node;
415 struct default_random_generator {
416 using gen_type = std::mt19937_64;
417 using result_type =
typename gen_type::result_type;
422 static thread_local gen_type engine(
423 static_cast<size_t>(time(0)));
428 static constexpr result_type
431 return gen_type::min();
434 static constexpr result_type
437 return gen_type::max();
441 template <
typename RndGenerator,
size_t MAX_LEVEL>
442 class geometric_level_generator {
444 using rnd_generator_type = RndGenerator;
446 static constexpr
size_t max_level = MAX_LEVEL;
453 static rnd_generator_type gen;
456 static thread_local std::geometric_distribution<size_t> d;
458 return (d(gen) % MAX_LEVEL) + 1;
490 template <
typename Traits>
493 using traits_type = Traits;
494 using key_type =
typename traits_type::key_type;
495 using mapped_type =
typename traits_type::mapped_type;
496 using value_type =
typename traits_type::value_type;
497 using size_type = std::size_t;
498 using difference_type = std::ptrdiff_t;
499 using key_compare =
typename traits_type::compare_type;
500 using allocator_type =
typename traits_type::allocator_type;
501 using allocator_traits_type = std::allocator_traits<allocator_type>;
503 using reference = value_type &;
504 using const_reference =
const value_type &;
505 using pointer =
typename allocator_traits_type::pointer;
506 using const_pointer =
typename allocator_traits_type::const_pointer;
508 using list_node_type = skip_list_node<value_type>;
510 using iterator = skip_list_iterator<list_node_type, false>;
511 using const_iterator = skip_list_iterator<list_node_type, true>;
513 using reverse_iterator = std::reverse_iterator<iterator>;
514 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
516 static constexpr size_type MAX_LEVEL = traits_type::max_level;
518 using random_level_generator_type = geometric_level_generator<
519 typename traits_type::random_generator_type, MAX_LEVEL>;
520 using node_allocator_type =
typename std::allocator_traits<
521 allocator_type>::template rebind_alloc<uint8_t>;
522 using node_allocator_traits =
typename std::allocator_traits<
523 allocator_type>::template rebind_traits<uint8_t>;
524 using node_ptr = list_node_type *;
525 using const_node_ptr =
const list_node_type *;
526 using persistent_node_ptr = persistent_pool_ptr<list_node_type>;
528 using prev_array_type = std::array<node_ptr, MAX_LEVEL>;
529 using next_array_type = std::array<persistent_node_ptr, MAX_LEVEL>;
530 using node_lock_type =
typename list_node_type::lock_type;
531 using lock_array = std::array<node_lock_type, MAX_LEVEL>;
534 static constexpr
bool allow_multimapping =
535 traits_type::allow_multimapping;
568 const key_compare &comp,
569 const allocator_type &alloc = allocator_type())
570 : _node_allocator(alloc), _compare(comp)
599 template <
class InputIt>
601 const key_compare &comp = key_compare(),
602 const allocator_type &alloc = allocator_type())
603 : _node_allocator(alloc), _compare(comp)
607 while (first != last)
629 : _node_allocator(node_allocator_traits::
630 select_on_container_copy_construction(
631 other._node_allocator)),
632 _compare(other._compare),
633 _rnd_generator(other._rnd_generator)
637 internal_copy(other);
638 assert(_size == other._size);
661 const allocator_type &alloc)
662 : _node_allocator(alloc),
663 _compare(other._compare),
664 _rnd_generator(other._rnd_generator)
668 internal_copy(other);
669 assert(_size == other._size);
691 : _node_allocator(std::move(other._node_allocator)),
692 _compare(other._compare),
693 _rnd_generator(other._rnd_generator)
697 internal_move(std::move(other));
720 const allocator_type &alloc)
721 : _node_allocator(alloc),
722 _compare(other._compare),
723 _rnd_generator(other._rnd_generator)
727 if (alloc == other.get_allocator()) {
728 internal_move(std::move(other));
731 internal_copy(std::make_move_iterator(other.begin()),
732 std::make_move_iterator(other.end()));
747 assert(this->
size() ==
748 size_type(std::distance(this->
begin(), this->
end())));
766 if (dummy_head ==
nullptr)
817 using pocca_t =
typename node_allocator_traits::
818 propagate_on_container_copy_assignment;
821 other._node_allocator,
823 _compare = other._compare;
824 _rnd_generator = other._rnd_generator;
826 internal_copy(other);
859 using pocma_t =
typename node_allocator_traits::
860 propagate_on_container_move_assignment;
862 if (pocma_t::value ||
863 _node_allocator == other._node_allocator) {
866 other._node_allocator,
868 _compare = other._compare;
869 _rnd_generator = other._rnd_generator;
870 internal_move(std::move(other));
873 std::make_move_iterator(other.begin()),
874 std::make_move_iterator(other.end()));
897 for (
auto it = il.begin(); it != il.end(); ++it)
919 std::pair<iterator, bool>
943 template <
typename P,
944 typename std::enable_if<
945 std::is_constructible<value_type, P &&>::value>::type>
946 std::pair<iterator, bool>
949 return emplace(std::forward<P>(value));
968 std::pair<iterator, bool>
992 insert(const_iterator hint, const_reference value)
995 return insert(value).first;
1018 template <
typename P,
1019 typename std::enable_if<
1020 std::is_constructible<value_type, P &&>::value>::type>
1041 template <
typename InputIterator>
1043 insert(InputIterator first, InputIterator last)
1045 for (InputIterator it = first; it != last; ++it)
1063 insert(std::initializer_list<value_type> ilist)
1065 insert(ilist.begin(), ilist.end());
1095 template <
typename... Args>
1096 std::pair<iterator, bool>
1099 return internal_emplace(std::forward<Args>(args)...);
1132 template <
typename... Args>
1137 return emplace(std::forward<Args>(args)...).first;
1163 template <
typename... Args>
1164 std::pair<iterator, bool>
1167 return internal_try_emplace(k, std::forward<Args>(args)...);
1193 template <
typename... Args>
1194 std::pair<iterator, bool>
1197 return internal_try_emplace(std::move(k),
1198 std::forward<Args>(args)...);
1227 template <
typename K,
typename... Args>
1228 typename std::enable_if<
1229 has_is_transparent<key_compare>::value &&
1230 std::is_constructible<key_type, K &&>::value,
1231 std::pair<iterator, bool>>::type
1234 return internal_try_emplace(std::forward<K>(k),
1235 std::forward<Args>(args)...);
1258 auto &size_diff = tls_data.
local().size_diff;
1259 return internal_erase(pos, size_diff);
1303 auto &size_diff = tls_data.
local().size_diff;
1306 while (first != last) {
1307 first = internal_erase(first, size_diff);
1311 return get_iterator(first);
1329 std::pair<iterator, iterator> range =
equal_range(key);
1330 size_type sz =
static_cast<size_type
>(
1331 std::distance(range.first, range.second));
1356 typename =
typename std::enable_if<
1357 has_is_transparent<key_compare>::value &&
1358 !std::is_convertible<K, iterator>::value &&
1359 !std::is_convertible<K, const_iterator>::value,
1364 std::pair<iterator, iterator> range =
equal_range(key);
1365 size_type sz =
static_cast<size_type
>(
1366 std::distance(range.first, range.second));
1416 template <
typename K,
1417 typename =
typename std::enable_if<
1418 has_is_transparent<key_compare>::value, K>::type>
1438 template <
typename K,
1439 typename =
typename std::enable_if<
1440 has_is_transparent<key_compare>::value, K>::type>
1492 template <
typename K,
1493 typename =
typename std::enable_if<
1494 has_is_transparent<key_compare>::value, K>::type>
1514 template <
typename K,
1515 typename =
typename std::enable_if<
1516 has_is_transparent<key_compare>::value, K>::type>
1534 return internal_find(key);
1548 return internal_find(key);
1563 template <
typename K,
1564 typename =
typename std::enable_if<
1565 has_is_transparent<key_compare>::value, K>::type>
1569 return internal_find(x);
1584 template <
typename K,
1585 typename =
typename std::enable_if<
1586 has_is_transparent<key_compare>::value, K>::type>
1590 return internal_find(x);
1605 return internal_count(key);
1620 template <
typename K,
1621 typename =
typename std::enable_if<
1622 has_is_transparent<key_compare>::value, K>::type>
1626 return internal_count(key);
1655 template <
typename K,
1656 typename =
typename std::enable_if<
1657 has_is_transparent<key_compare>::value, K>::type>
1675 assert(dummy_head(pool_uuid)->height() > 0);
1678 persistent_node_ptr current = dummy_head(pool_uuid)->next(0);
1682 assert(current(pool_uuid)->height() > 0);
1683 persistent_node_ptr next =
1684 current(pool_uuid)->next(0);
1685 delete_node(current);
1689 node_ptr head = dummy_head.get(pool_uuid);
1690 for (size_type i = 0; i < head->height(); ++i) {
1691 head->set_next(i,
nullptr);
1712 dummy_head.get(pool_uuid)->next(0).get(pool_uuid));
1724 return const_iterator(
1726 dummy_head.get(pool_uuid)->next(0).get(pool_uuid));
1738 return const_iterator(
1740 dummy_head.get(pool_uuid)->next(0).get(pool_uuid));
1753 return iterator(pool_uuid,
nullptr);
1766 return const_iterator(pool_uuid,
nullptr);
1779 return const_iterator(pool_uuid,
nullptr);
1791 return _size.load(std::memory_order_relaxed);
1804 return std::numeric_limits<difference_type>::max();
1825 const allocator_type &
1828 return _node_allocator;
1839 return _node_allocator;
1854 using pocs_t =
typename node_allocator_traits::
1855 propagate_on_container_swap;
1859 std::swap(_rnd_generator, other._rnd_generator);
1860 std::swap(dummy_head, other.dummy_head);
1865 _size = other._size.exchange(_size,
1866 std::memory_order_relaxed);
1889 std::pair<iterator, iterator>
1892 return std::pair<iterator, iterator>(
lower_bound(key),
1915 std::pair<const_iterator, const_iterator>
1918 return std::pair<const_iterator, const_iterator>(
1944 template <
typename K,
1945 typename =
typename std::enable_if<
1946 has_is_transparent<key_compare>::value, K>::type>
1947 std::pair<iterator, iterator>
1950 return std::pair<iterator, iterator>(
lower_bound(x),
1976 template <
typename K,
1977 typename =
typename std::enable_if<
1978 has_is_transparent<key_compare>::value, K>::type>
1979 std::pair<const_iterator, const_iterator>
1982 return std::pair<const_iterator, const_iterator>(
2010 enum insert_stage_type : uint8_t { not_started = 0, in_progress = 1 };
2015 struct tls_entry_type {
2016 persistent_node_ptr ptr;
2020 char reserved[64 -
sizeof(decltype(ptr)) -
2021 sizeof(decltype(size_diff)) -
2022 sizeof(decltype(insert_stage))];
2024 static_assert(
sizeof(tls_entry_type) == 64,
2025 "The size of tls_entry_type should be 64 bytes.");
2037 if (pmemobj_tx_stage() != TX_STAGE_WORK)
2039 "Function called out of transaction scope.");
2046 if (pmemobj_tx_stage() != TX_STAGE_NONE)
2048 "Function called inside transaction scope.");
2065 assert(this->
empty());
2066 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2067 dummy_head = other.dummy_head;
2068 other.dummy_head =
nullptr;
2069 other.create_dummy_head();
2071 _size.store(other._size.load(std::memory_order_relaxed),
2072 std::memory_order_relaxed);
2076 static const_reference
2077 get_val(const_node_ptr n)
2090 static const key_type &
2091 get_key(const_node_ptr n)
2094 return traits_type::get_key(get_val(n));
2097 template <
typename K>
2099 internal_find(
const K &key)
2102 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2107 template <
typename K>
2109 internal_find(
const K &key)
const
2112 return (it ==
end() || _compare(key, traits_type::get_key(*it)))
2117 template <
typename K>
2119 internal_count(
const K &key)
const
2121 if (allow_multimapping) {
2122 std::pair<const_iterator, const_iterator> range =
2124 return static_cast<size_type
>(
2125 std::distance(range.first, range.second));
2127 return (
find(key) ==
end()) ? size_type(0) : size_type(1);
2140 template <
typename K,
typename po
inter_type,
typename comparator>
2143 const K &key,
const comparator &cmp)
const
2145 assert(level < prev->height());
2146 persistent_node_ptr next = prev->next(level);
2147 pointer_type curr = next.get(pool_uuid);
2149 while (curr && cmp(get_key(curr), key)) {
2151 assert(level < prev->height());
2152 next = prev->next(level);
2153 curr = next.get(pool_uuid);
2169 template <
typename K>
2172 next_array_type &next_nodes,
const K &key)
2174 if (allow_multimapping) {
2176 not_greater_compare(_compare));
2194 template <
typename K,
typename comparator>
2197 next_array_type &next_nodes,
const K &key,
2198 const comparator &cmp)
2200 node_ptr prev = dummy_head.get(pool_uuid);
2201 prev_nodes.fill(prev);
2202 next_nodes.fill(
nullptr);
2204 for (size_type h = prev->height(); h > 0; --h) {
2205 persistent_node_ptr next =
2207 prev_nodes[h - 1] = prev;
2208 next_nodes[h - 1] = next;
2212 template <
typename K,
typename... Args>
2213 std::pair<iterator, bool>
2214 internal_try_emplace(K &&key, Args &&... args)
2217 key, std::piecewise_construct,
2218 std::forward_as_tuple(std::forward<K>(key)),
2219 std::forward_as_tuple(std::forward<Args>(args)...));
2222 template <
typename... Args>
2223 std::pair<iterator, bool>
2224 internal_emplace(Args &&... args)
2227 tls_entry_type &tls_entry = tls_data.
local();
2231 assert(tls_entry.ptr ==
nullptr);
2234 ++tls_entry.size_diff;
2235 tls_entry.insert_stage = not_started;
2238 node_ptr n = tls_entry.ptr.get(pool_uuid);
2239 size_type height = n->height();
2243 [&](
const next_array_type &next_nodes)
2244 -> persistent_node_ptr & {
2245 assert(tls_entry.insert_stage == not_started);
2246 assert(tls_entry.ptr !=
nullptr);
2248 n->set_nexts(pop, next_nodes.data(), height);
2250 tls_entry.insert_stage = in_progress;
2251 pop.
persist(&(tls_entry.insert_stage),
2252 sizeof(tls_entry.insert_stage));
2254 return tls_entry.ptr;
2257 if (!insert_result.second) {
2258 assert(tls_entry.ptr !=
nullptr);
2259 assert(tls_entry.insert_stage == not_started);
2262 --tls_entry.size_diff;
2263 delete_node(tls_entry.ptr);
2264 tls_entry.ptr =
nullptr;
2268 assert(tls_entry.ptr ==
nullptr);
2269 return insert_result;
2276 template <
typename... Args>
2277 std::pair<iterator, bool>
2282 persistent_node_ptr new_node =
2285 node_ptr n = new_node.get(pool_uuid);
2286 size_type height = n->height();
2290 [&](
const next_array_type &next_nodes)
2291 -> persistent_node_ptr & {
2292 assert(new_node !=
nullptr);
2294 n->set_nexts(next_nodes.data(), height);
2299 if (insert_result.second) {
2302 assert(new_node !=
nullptr);
2304 delete_node(new_node);
2307 return insert_result;
2313 template <
typename K,
typename... Args>
2314 std::pair<iterator, bool>
2318 tls_entry_type &tls_entry = tls_data.
local();
2319 assert(tls_entry.ptr ==
nullptr);
2325 [&](
const next_array_type &next_nodes)
2326 -> persistent_node_ptr & {
2331 std::forward_as_tuple(
2332 height, next_nodes.data()),
2333 std::forward_as_tuple(
2334 std::forward<Args>(args)...));
2336 ++(tls_entry.size_diff);
2337 tls_entry.insert_stage = in_progress;
2340 assert(tls_entry.ptr !=
nullptr);
2341 return tls_entry.ptr;
2344 assert(tls_entry.ptr ==
nullptr);
2346 return insert_result;
2352 template <
typename K,
typename PrepareNode>
2353 std::pair<iterator, bool>
2355 PrepareNode &&prepare_new_node)
2357 prev_array_type prev_nodes;
2358 next_array_type next_nodes;
2359 node_ptr n =
nullptr;
2364 node_ptr next = next_nodes[0].get(pool_uuid);
2365 if (next && !allow_multimapping &&
2366 !_compare(key, get_key(next))) {
2368 return std::pair<iterator, bool>(
2369 iterator(pool_uuid, next),
false);
2373 std::forward<PrepareNode>(
2374 prepare_new_node))) ==
2378 return std::pair<iterator, bool>(iterator(pool_uuid, n),
true);
2386 template <
typename PrepareNode>
2389 const next_array_type &next_nodes, size_type height,
2390 PrepareNode &&prepare_new_node)
2392 assert(dummy_head(pool_uuid)->height() >= height);
2395 if (!try_lock_nodes(height, prev_nodes, next_nodes, locks)) {
2399 node_lock_type new_node_lock;
2401 persistent_node_ptr &new_node = prepare_new_node(next_nodes);
2402 assert(new_node !=
nullptr);
2403 node_ptr n = new_node.get(pool_uuid);
2411 new_node_lock = n->acquire();
2423 for (size_type level = 0; level < height; ++level) {
2424 assert(prev_nodes[level]->height() > level);
2425 assert(prev_nodes[level]->next(level) ==
2427 assert(prev_nodes[level]->next(level) ==
2429 prev_nodes[level]->set_next(pop, level, new_node);
2433 try_insert_node_finish_marker();
2440 pop.
persist(&new_node,
sizeof(new_node));
2443 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2444 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
2458 for (size_type l = 1; l < height; ++l) {
2459 if (prevs[l] == dummy_head.get(pool_uuid)) {
2463 assert(prevs[l - 1] != dummy_head.get(pool_uuid));
2464 assert(!_compare(get_key(prevs[l - 1]),
2465 get_key(prevs[l])));
2472 try_lock_nodes(size_type height, prev_array_type &prevs,
2473 const next_array_type &nexts, lock_array &locks)
2477 for (size_type l = 0; l < height; ++l) {
2478 if (l == 0 || prevs[l] != prevs[l - 1]) {
2479 locks[l] = prevs[l]->acquire();
2482 persistent_node_ptr next = prevs[l]->next(l);
2483 if (next != nexts[l])
2504 template <
typename K,
typename comparator>
2508 const_node_ptr prev = dummy_head.get(pool_uuid);
2509 assert(prev->height() > 0);
2510 persistent_node_ptr next =
nullptr;
2512 for (size_type h = prev->height(); h > 0; --h) {
2516 return const_iterator(pool_uuid, next.get(pool_uuid));
2530 template <
typename K,
typename comparator>
2534 node_ptr prev = dummy_head.get(pool_uuid);
2535 assert(prev->height() > 0);
2536 persistent_node_ptr next =
nullptr;
2538 for (size_type h = prev->height(); h > 0; --h) {
2542 return iterator(pool_uuid, next.get(pool_uuid));
2548 assert(pos !=
end());
2552 std::pair<persistent_node_ptr, persistent_node_ptr>
2553 extract_result(
nullptr,
nullptr);
2559 assert(extract_result.first !=
nullptr);
2560 delete_node(extract_result.first);
2566 return iterator(pool_uuid,
2567 extract_result.second.get(pool_uuid));
2573 std::pair<persistent_node_ptr, persistent_node_ptr>
2576 assert(dummy_head(pool_uuid)->height() > 0);
2577 assert(it !=
end());
2578 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2580 const key_type &key = traits_type::get_key(*it);
2582 prev_array_type prev_nodes;
2583 next_array_type next_nodes;
2587 node_ptr erase_node = next_nodes[0].get(pool_uuid);
2588 assert(erase_node !=
nullptr);
2590 if (!_compare(key, get_key(erase_node))) {
2594 assert(erase_node == it.node);
2595 return internal_extract_node(prev_nodes, next_nodes,
2599 return std::pair<persistent_node_ptr, persistent_node_ptr>(
2603 std::pair<persistent_node_ptr, persistent_node_ptr>
2604 internal_extract_node(
const prev_array_type &prev_nodes,
2605 const next_array_type &next_nodes,
2606 node_ptr erase_node)
2608 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2609 assert(erase_node !=
nullptr);
2610 for (size_type level = 0; level < erase_node->height();
2612 assert(prev_nodes[level]->height() > level);
2613 assert(next_nodes[level].
get(pool_uuid) == erase_node);
2614 prev_nodes[level]->set_next(level,
2615 erase_node->next(level));
2618 return std::pair<persistent_node_ptr, persistent_node_ptr>(
2619 next_nodes[0], erase_node->next(0));
2629 PMEMobjpool *pop = pmemobj_pool_by_ptr(
this);
2636 internal_copy(other.
begin(), other.
end());
2639 template <
typename Iterator>
2641 internal_copy(Iterator first, Iterator last)
2643 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2645 prev_array_type prev_nodes;
2646 prev_nodes.fill(dummy_head.get(pool_uuid));
2649 for (; first != last; ++first, ++sz) {
2650 persistent_node_ptr new_node =
create_node(*first);
2651 node_ptr n = new_node.get(pool_uuid);
2652 for (size_type level = 0; level < n->height();
2654 prev_nodes[level]->set_next(level, new_node);
2655 prev_nodes[level] = n;
2667 assert(std::is_sorted(
2669 [&](
const value_type &lhs,
const value_type &rhs) {
2670 return lhs.first < rhs.first;
2678 return _rnd_generator();
2682 calc_node_size(size_type height)
2684 return sizeof(list_node_type) +
2685 height *
sizeof(
typename list_node_type::node_pointer);
2689 template <
typename... Args>
2696 std::forward_as_tuple(levels),
2697 std::forward_as_tuple(std::forward<Args>(args)...));
2700 template <
typename... NodeArgs,
typename... ValueArgs>
2703 std::tuple<ValueArgs...> &&value_args)
2705 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2707 persistent_node_ptr node = creates_dummy_node(
2708 std::forward<std::tuple<NodeArgs...>>(node_args),
2709 index_sequence_for<NodeArgs...>{});
2711 construct_value_type(
2713 std::forward<std::tuple<ValueArgs...>>(value_args),
2714 index_sequence_for<ValueArgs...>{});
2719 template <
typename Tuple,
size_t... I>
2721 construct_value_type(persistent_node_ptr node, Tuple &&args,
2722 index_sequence<I...>)
2724 node_ptr new_node = node.get(pool_uuid);
2726 node_allocator_traits::construct(
2727 _node_allocator, new_node->get(),
2728 std::get<I>(std::forward<Tuple>(args))...);
2739 dummy_head = creates_dummy_node(MAX_LEVEL);
2742 template <
typename Tuple,
size_t... I>
2744 creates_dummy_node(Tuple &&args, index_sequence<I...>)
2746 return creates_dummy_node(
2747 std::get<I>(std::forward<Tuple>(args))...);
2759 template <
typename... Args>
2763 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2764 size_type sz = calc_node_size(height);
2766 persistent_node_ptr n =
2767 node_allocator_traits::allocate(_node_allocator, sz)
2770 assert(n !=
nullptr);
2772 node_allocator_traits::construct(_node_allocator,
2773 n.get(pool_uuid), height,
2774 std::forward<Args>(args)...);
2779 template <
bool is_dummy = false>
2781 delete_node(persistent_node_ptr &node)
2783 assert(pmemobj_tx_stage() == TX_STAGE_WORK);
2784 node_ptr n = node.get(pool_uuid);
2785 size_type sz = calc_node_size(n->height());
2789 node_allocator_traits::destroy(_node_allocator,
2792 node_allocator_traits::destroy(_node_allocator, n);
2794 deallocate_node(node, sz);
2799 deallocate_node(persistent_node_ptr &node, size_type sz)
2808 node.get_persistent_ptr(pool_uuid).raw();
2809 node_allocator_traits::deallocate(_node_allocator, tmp, sz);
2815 assert(dummy_head !=
nullptr);
2816 delete_node<true>(dummy_head);
2817 assert(dummy_head ==
nullptr);
2821 get_iterator(const_iterator it)
2825 const_cast<typename iterator::node_ptr
>(it.node));
2832 int64_t last_run_size = 0;
2835 for (
auto &tls_entry : tls_data) {
2836 persistent_node_ptr &node = tls_entry.ptr;
2837 auto &size_diff = tls_entry.size_diff;
2849 if (tls_entry.insert_stage == in_progress) {
2850 complete_insert(tls_entry);
2853 --(tls_entry.size_diff);
2860 assert(node ==
nullptr);
2862 last_run_size += size_diff;
2866 assert(last_run_size >= 0 ||
2868 static_cast<size_type
>(std::abs(last_run_size)));
2874 #if LIBPMEMOBJ_CPP_VG_PMEMCHECK_ENABLED
2875 VALGRIND_PMC_DO_FLUSH(&_size,
sizeof(_size));
2880 complete_insert(tls_entry_type &tls_entry)
2882 persistent_node_ptr &node = tls_entry.ptr;
2883 assert(node !=
nullptr);
2884 assert(tls_entry.insert_stage == in_progress);
2885 prev_array_type prev_nodes;
2886 next_array_type next_nodes;
2887 node_ptr n = node.get(pool_uuid);
2888 const key_type &key = get_key(n);
2889 size_type height = n->height();
2895 for (size_type level = 0; level < height; ++level) {
2896 assert(prev_nodes[level]->height() > level);
2897 assert(prev_nodes[level]->next(level) ==
2900 if (prev_nodes[level]->next(level) != node) {
2903 assert(n->next(level) == next_nodes[level]);
2904 prev_nodes[level]->set_next(pop, level, node);
2909 pop.
persist(&node,
sizeof(node));
2912 struct not_greater_compare {
2913 const key_compare &my_less_compare;
2915 not_greater_compare(
const key_compare &less_compare)
2916 : my_less_compare(less_compare)
2920 template <
typename K1,
typename K2>
2922 operator()(
const K1 &first,
const K2 &second)
const
2924 return !my_less_compare(second, first);
2928 const uint64_t pool_uuid = pmemobj_oid(
this).pool_uuid_lo;
2929 node_allocator_type _node_allocator;
2930 key_compare _compare;
2931 random_level_generator_type _rnd_generator;
2932 persistent_node_ptr dummy_head;
2934 enumerable_thread_specific<tls_entry_type> tls_data;
2936 std::atomic<size_type> _size;
2946 template <
typename Key,
typename Value,
typename KeyCompare,
2947 typename RND_GENERATOR,
typename Allocator,
bool AllowMultimapping,
2951 static constexpr
size_t max_level = MAX_LEVEL;
2952 using random_generator_type = RND_GENERATOR;
2953 using key_type = Key;
2954 using mapped_type = Value;
2955 using compare_type = KeyCompare;
2956 using value_type = pair<const key_type, mapped_type>;
2957 using reference = value_type &;
2958 using const_reference =
const value_type &;
2959 using allocator_type = Allocator;
2967 constexpr
static bool allow_multimapping = AllowMultimapping;
2969 static const key_type &
2970 get_key(const_reference val)