Skip to content
This repository was archived by the owner on Mar 22, 2023. It is now read-only.

Commit 8a1aec4

Browse files
Merge pull request #1099 from igchor/radix_tree_concurrent_fix
Radix tree concurrent fix
2 parents 2c69142 + 0dd6afe commit 8a1aec4

1 file changed

Lines changed: 122 additions & 63 deletions

File tree

include/libpmemobj++/experimental/radix_tree.hpp

Lines changed: 122 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -333,9 +333,9 @@ class radix_tree {
333333
static bool keys_equal(const K1 &k1, const K2 &k2);
334334
template <typename K1, typename K2>
335335
static int compare(const K1 &k1, const K2 &k2, byten_t offset = 0);
336-
template <bool Direction, typename Iterator, typename K>
337-
const leaf *next_leaf(Iterator child, pointer_type parent,
338-
const K &k) const;
336+
template <bool Direction, typename Iterator>
337+
std::pair<bool, const leaf *> next_leaf(Iterator child,
338+
pointer_type parent) const;
339339
template <bool Direction>
340340
const leaf *find_leaf(pointer_type n) const;
341341
static unsigned slice_index(char k, uint8_t shift);
@@ -619,6 +619,9 @@ struct radix_tree<Key, Value, BytesView>::radix_tree_iterator {
619619

620620
template <typename T>
621621
void replace_val(T &&rhs);
622+
623+
bool try_increment();
624+
bool try_decrement();
622625
};
623626

624627
template <typename Key, typename Value, typename BytesView>
@@ -2108,6 +2111,9 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21082111
auto key = bytes_view(k);
21092112
auto pop = pool_base(pmemobj_pool_by_ptr(this));
21102113

2114+
path_type path;
2115+
const_iterator result;
2116+
21112117
while (true) {
21122118
auto r = root.load_acquire();
21132119

@@ -2123,7 +2129,7 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21232129
*/
21242130
auto ret = descend(r, key);
21252131
auto leaf = ret.first;
2126-
auto path = ret.second;
2132+
path = ret.second;
21272133

21282134
if (!leaf)
21292135
continue;
@@ -2134,10 +2140,17 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21342140

21352141
/* Key exists. */
21362142
if (diff == key.size() && leaf_key.size() == key.size()) {
2143+
result = const_iterator(leaf, this);
2144+
2145+
/* For lower_bound, result is looked-for element. */
21372146
if (Lower)
2138-
return const_iterator(leaf, this);
2139-
else
2140-
return ++const_iterator(leaf, this);
2147+
break;
2148+
2149+
/* For upper_bound, we need to find larger element. */
2150+
if (result.try_increment())
2151+
break;
2152+
2153+
continue;
21412154
}
21422155

21432156
/* Descend the tree again by following the path. */
@@ -2147,8 +2160,6 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21472160
auto slot = node_d.slot;
21482161
auto prev = node_d.prev;
21492162

2150-
const_iterator result;
2151-
21522163
/*
21532164
* n would point to element with key which we are looking for
21542165
* (if it existed). All elements on the left are smaller and all
@@ -2160,9 +2171,12 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21602171
auto target_leaf = next_leaf<node::direction::Forward>(
21612172
prev->template make_iterator<
21622173
node::direction::Forward>(slot),
2163-
prev, k);
2174+
prev);
21642175

2165-
result = const_iterator(target_leaf, this);
2176+
if (!target_leaf.first)
2177+
continue;
2178+
2179+
result = const_iterator(target_leaf.second, this);
21662180
} else if (diff == key.size()) {
21672181
/* The looked-for key is a prefix of the leaf key. The
21682182
* target node must be the smallest leaf within *slot's
@@ -2178,10 +2192,24 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21782192

21792193
result = const_iterator(target_leaf, this);
21802194
} else if (diff == leaf_key.size()) {
2195+
assert(n == leaf);
2196+
21812197
/* Leaf's key is a prefix of the looked-for key. Leaf's
21822198
* key is the biggest key less than the looked-for key.
2183-
* The target node must be the next leaf. */
2184-
result = ++const_iterator(leaf, this);
2199+
* The target node must be the next leaf. Note that we
2200+
* cannot just call const_iterator(leaf,
2201+
* this).try_increment() because some other element with
2202+
* key larger than leaf and smaller than k could be
2203+
* inserted concurrently. */
2204+
auto target_leaf = next_leaf<node::direction::Forward>(
2205+
prev->template make_iterator<
2206+
node::direction::Forward>(slot),
2207+
prev);
2208+
2209+
if (!target_leaf.first)
2210+
continue;
2211+
2212+
result = const_iterator(target_leaf.second, this);
21852213
} else {
21862214
assert(diff < leaf_key.size() && diff < key.size());
21872215

@@ -2236,21 +2264,25 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
22362264
node::direction::Forward>(
22372265
prev->template make_iterator<
22382266
node::direction::Forward>(slot),
2239-
prev, k);
2267+
prev);
22402268

2241-
result = const_iterator(target_leaf, this);
2269+
if (!target_leaf.first)
2270+
continue;
2271+
2272+
result = const_iterator(target_leaf.second,
2273+
this);
22422274
}
22432275
}
22442276

22452277
/* If some node on the path was modified, the calculated result
22462278
* might not be correct. */
2247-
if (!validate_path(path))
2248-
continue;
2279+
if (validate_path(path))
2280+
break;
2281+
}
22492282

2250-
assert(validate_bound<Lower>(result, k));
2283+
assert(validate_bound<Lower>(result, k));
22512284

2252-
return result;
2253-
}
2285+
return result;
22542286
}
22552287

22562288
/**
@@ -2938,35 +2970,69 @@ template <bool IsConst>
29382970
typename radix_tree<Key, Value,
29392971
BytesView>::template radix_tree_iterator<IsConst> &
29402972
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator++()
2973+
{
2974+
/* Fallback to top-down search. */
2975+
if (!try_increment())
2976+
*this = tree->upper_bound(leaf_->key());
2977+
2978+
return *this;
2979+
}
2980+
2981+
/*
2982+
* Tries to increment iterator. Returns true on success, false otherwise.
2983+
* Increment can fail in case of concurrent, conflicting operation.
2984+
*/
2985+
template <typename Key, typename Value, typename BytesView>
2986+
template <bool IsConst>
2987+
bool
2988+
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::try_increment()
29412989
{
29422990
assert(leaf_);
29432991

29442992
constexpr auto direction = radix_tree::node::direction::Forward;
29452993
auto parent_ptr = leaf_->parent.load_acquire();
29462994

29472995
/* leaf is root, there is no other leaf in the tree */
2948-
if (!parent_ptr)
2996+
if (!parent_ptr) {
29492997
leaf_ = nullptr;
2950-
else {
2998+
} else {
29512999
auto it = parent_ptr->template find_child<direction>(leaf_);
29523000

2953-
if (it == parent_ptr->template end<direction>()) {
2954-
*this = tree->upper_bound(leaf_->key());
2955-
} else {
2956-
leaf_ = const_cast<leaf_ptr>(
2957-
tree->template next_leaf<direction>(
2958-
it, parent_ptr, leaf_->key()));
2959-
}
3001+
if (it == parent_ptr->template end<direction>())
3002+
return false;
3003+
3004+
auto ret = tree->template next_leaf<direction>(it, parent_ptr);
3005+
3006+
if (!ret.first)
3007+
return false;
3008+
3009+
leaf_ = const_cast<leaf_ptr>(ret.second);
29603010
}
29613011

2962-
return *this;
3012+
return true;
29633013
}
29643014

29653015
template <typename Key, typename Value, typename BytesView>
29663016
template <bool IsConst>
29673017
typename radix_tree<Key, Value,
29683018
BytesView>::template radix_tree_iterator<IsConst> &
29693019
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator--()
3020+
{
3021+
while (!try_decrement()) {
3022+
*this = tree->lower_bound(leaf_->key());
3023+
}
3024+
3025+
return *this;
3026+
}
3027+
3028+
/*
3029+
* Tries to decrement iterator. Returns true on success, false otherwise.
3030+
* Decrement can fail in case of concurrent, conflicting operation.
3031+
*/
3032+
template <typename Key, typename Value, typename BytesView>
3033+
template <bool IsConst>
3034+
bool
3035+
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::try_decrement()
29703036
{
29713037
constexpr auto direction = radix_tree::node::direction::Reverse;
29723038

@@ -2982,7 +3048,7 @@ radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator--()
29823048
tree->template find_leaf<direction>(r));
29833049

29843050
if (leaf_)
2985-
return *this;
3051+
return true;
29863052
} else {
29873053
auto parent_ptr = leaf_->parent.load_acquire();
29883054

@@ -2992,18 +3058,17 @@ radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator--()
29923058
auto it = parent_ptr->template find_child<direction>(
29933059
leaf_);
29943060

2995-
if (it == parent_ptr->template end<direction>()) {
2996-
/* Search the tree from top - this iterator
2997-
* might point to detached leaf. */
2998-
*this = --tree->lower_bound(leaf_->key());
2999-
return *this;
3000-
}
3061+
if (it == parent_ptr->template end<direction>())
3062+
return false;
30013063

3002-
leaf_ = const_cast<leaf_ptr>(
3003-
tree->template next_leaf<direction>(
3004-
it, parent_ptr, leaf_->key()));
3064+
auto ret = tree->template next_leaf<direction>(
3065+
it, parent_ptr);
3066+
3067+
if (!ret.first)
3068+
return false;
30053069

3006-
return *this;
3070+
leaf_ = const_cast<leaf_ptr>(ret.second);
3071+
return true;
30073072
}
30083073
}
30093074
}
@@ -3055,42 +3120,36 @@ radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator==(
30553120
}
30563121

30573122
/*
3058-
* Returns next leaf (either with smaller or larger key than k, depending on
3123+
* Returns a pair consisting of a bool and a leaf pointer to the
3124+
* next leaf (either with smaller or larger key than k, depending on
30593125
* ChildIterator type). This function might need to traverse the
3060-
* tree upwards. Return value can be null if there is no such leaf.
3126+
* tree upwards. Pointer can be null if there is no such leaf.
30613127
*
3062-
* Parameter k is only used in case of confliciting, concurrent operation
3063-
* to restart from the top.
3128+
* Bool variable is set to true on success, false otherwise.
3129+
* Failure can occur in case of a concurrent, conflicting operation.
30643130
*/
30653131
template <typename Key, typename Value, typename BytesView>
3066-
template <bool Direction, typename Iterator, typename K>
3067-
const typename radix_tree<Key, Value, BytesView>::leaf *
3068-
radix_tree<Key, Value, BytesView>::next_leaf(Iterator node, pointer_type parent,
3069-
const K &k) const
3132+
template <bool Direction, typename Iterator>
3133+
std::pair<bool, const typename radix_tree<Key, Value, BytesView>::leaf *>
3134+
radix_tree<Key, Value, BytesView>::next_leaf(Iterator node,
3135+
pointer_type parent) const
30703136
{
3071-
auto fallback = [&] {
3072-
if (Direction == node::direction::Forward)
3073-
return upper_bound(k).leaf_;
3074-
else
3075-
return (--lower_bound(k)).leaf_;
3076-
};
3077-
30783137
while (true) {
30793138
++node;
30803139

30813140
/* No more children on this level, need to go up. */
30823141
if (node == parent->template end<Direction>()) {
30833142
auto p = parent->parent.load_acquire();
3084-
if (!p) // parent == root
3085-
return nullptr;
3143+
if (!p) /* parent == root */
3144+
return {true, nullptr};
30863145

30873146
auto p_it = p->template find_child<Direction>(parent);
30883147
if (p_it == p->template end<Direction>()) {
3089-
/* Detached leaf, need to start from the top. */
3090-
return fallback();
3148+
/* Detached leaf, cannot advance. */
3149+
return {false, nullptr};
30913150
}
30923151

3093-
return next_leaf<Direction>(p_it, p, k);
3152+
return next_leaf<Direction>(p_it, p);
30943153
}
30953154

30963155
auto n = node->load_acquire();
@@ -3099,9 +3158,9 @@ radix_tree<Key, Value, BytesView>::next_leaf(Iterator node, pointer_type parent,
30993158

31003159
auto leaf = find_leaf<Direction>(n);
31013160
if (!leaf)
3102-
return fallback();
3161+
return {false, nullptr};
31033162

3104-
return leaf;
3163+
return {true, leaf};
31053164
}
31063165
}
31073166

0 commit comments

Comments
 (0)