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

Commit 45bc3b6

Browse files
committed
radix_tree: get rid of recursion
in radix_tree_iterator::operator++/-- <-> internal_bound. The recursion caused stack overflow errors, e.g. : #1091
1 parent 4808295 commit 45bc3b6

1 file changed

Lines changed: 109 additions & 62 deletions

File tree

include/libpmemobj++/experimental/radix_tree.hpp

Lines changed: 109 additions & 62 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
@@ -2181,7 +2195,9 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
21812195
/* Leaf's key is a prefix of the looked-for key. Leaf's
21822196
* key is the biggest key less than the looked-for key.
21832197
* The target node must be the next leaf. */
2184-
result = ++const_iterator(leaf, this);
2198+
result = const_iterator(leaf, this);
2199+
if (!result.try_increment())
2200+
continue;
21852201
} else {
21862202
assert(diff < leaf_key.size() && diff < key.size());
21872203

@@ -2236,21 +2252,25 @@ radix_tree<Key, Value, BytesView>::internal_bound(const K &k) const
22362252
node::direction::Forward>(
22372253
prev->template make_iterator<
22382254
node::direction::Forward>(slot),
2239-
prev, k);
2255+
prev);
22402256

2241-
result = const_iterator(target_leaf, this);
2257+
if (!target_leaf.first)
2258+
continue;
2259+
2260+
result = const_iterator(target_leaf.second,
2261+
this);
22422262
}
22432263
}
22442264

22452265
/* If some node on the path was modified, the calculated result
22462266
* might not be correct. */
2247-
if (!validate_path(path))
2248-
continue;
2267+
if (validate_path(path))
2268+
break;
2269+
}
22492270

2250-
assert(validate_bound<Lower>(result, k));
2271+
assert(validate_bound<Lower>(result, k));
22512272

2252-
return result;
2253-
}
2273+
return result;
22542274
}
22552275

22562276
/**
@@ -2938,35 +2958,69 @@ template <bool IsConst>
29382958
typename radix_tree<Key, Value,
29392959
BytesView>::template radix_tree_iterator<IsConst> &
29402960
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator++()
2961+
{
2962+
/* Fallback to top-down search. */
2963+
if (!try_increment())
2964+
*this = tree->upper_bound(leaf_->key());
2965+
2966+
return *this;
2967+
}
2968+
2969+
/*
2970+
* Tries to increment iterator. Returns true on success, false otherwise.
2971+
* Increment can fail in case of concurrent, conflicting operation.
2972+
*/
2973+
template <typename Key, typename Value, typename BytesView>
2974+
template <bool IsConst>
2975+
bool
2976+
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::try_increment()
29412977
{
29422978
assert(leaf_);
29432979

29442980
constexpr auto direction = radix_tree::node::direction::Forward;
29452981
auto parent_ptr = leaf_->parent.load_acquire();
29462982

29472983
/* leaf is root, there is no other leaf in the tree */
2948-
if (!parent_ptr)
2984+
if (!parent_ptr) {
29492985
leaf_ = nullptr;
2950-
else {
2986+
} else {
29512987
auto it = parent_ptr->template find_child<direction>(leaf_);
29522988

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-
}
2989+
if (it == parent_ptr->template end<direction>())
2990+
return false;
2991+
2992+
auto ret = tree->template next_leaf<direction>(it, parent_ptr);
2993+
2994+
if (!ret.first)
2995+
return false;
2996+
2997+
leaf_ = const_cast<leaf_ptr>(ret.second);
29602998
}
29612999

2962-
return *this;
3000+
return true;
29633001
}
29643002

29653003
template <typename Key, typename Value, typename BytesView>
29663004
template <bool IsConst>
29673005
typename radix_tree<Key, Value,
29683006
BytesView>::template radix_tree_iterator<IsConst> &
29693007
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator--()
3008+
{
3009+
while (!try_decrement()) {
3010+
*this = tree->lower_bound(leaf_->key());
3011+
}
3012+
3013+
return *this;
3014+
}
3015+
3016+
/*
3017+
* Tries to decrement iterator. Returns true on success, false otherwise.
3018+
* Decrement can fail in case of concurrent, conflicting operation.
3019+
*/
3020+
template <typename Key, typename Value, typename BytesView>
3021+
template <bool IsConst>
3022+
bool
3023+
radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::try_decrement()
29703024
{
29713025
constexpr auto direction = radix_tree::node::direction::Reverse;
29723026

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

29843038
if (leaf_)
2985-
return *this;
3039+
return true;
29863040
} else {
29873041
auto parent_ptr = leaf_->parent.load_acquire();
29883042

@@ -2992,18 +3046,17 @@ radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator--()
29923046
auto it = parent_ptr->template find_child<direction>(
29933047
leaf_);
29943048

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-
}
3049+
if (it == parent_ptr->template end<direction>())
3050+
return false;
30013051

3002-
leaf_ = const_cast<leaf_ptr>(
3003-
tree->template next_leaf<direction>(
3004-
it, parent_ptr, leaf_->key()));
3052+
auto ret = tree->template next_leaf<direction>(
3053+
it, parent_ptr);
30053054

3006-
return *this;
3055+
if (!ret.first)
3056+
return false;
3057+
3058+
leaf_ = const_cast<leaf_ptr>(ret.second);
3059+
return true;
30073060
}
30083061
}
30093062
}
@@ -3055,42 +3108,36 @@ radix_tree<Key, Value, BytesView>::radix_tree_iterator<IsConst>::operator==(
30553108
}
30563109

30573110
/*
3058-
* Returns next leaf (either with smaller or larger key than k, depending on
3111+
* Returns a pair consisting of a bool and a leaf pointer to the
3112+
* next leaf (either with smaller or larger key than k, depending on
30593113
* ChildIterator type). This function might need to traverse the
3060-
* tree upwards. Return value can be null if there is no such leaf.
3114+
* tree upwards. Pointer can be null if there is no such leaf.
30613115
*
3062-
* Parameter k is only used in case of confliciting, concurrent operation
3063-
* to restart from the top.
3116+
* Bool variable is set to true on success, false otherwise.
3117+
* Failure can occur in case of a concurrent, conflicting operation.
30643118
*/
30653119
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
3120+
template <bool Direction, typename Iterator>
3121+
std::pair<bool, const typename radix_tree<Key, Value, BytesView>::leaf *>
3122+
radix_tree<Key, Value, BytesView>::next_leaf(Iterator node,
3123+
pointer_type parent) const
30703124
{
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-
30783125
while (true) {
30793126
++node;
30803127

30813128
/* No more children on this level, need to go up. */
30823129
if (node == parent->template end<Direction>()) {
30833130
auto p = parent->parent.load_acquire();
3084-
if (!p) // parent == root
3085-
return nullptr;
3131+
if (!p) /* parent == root */
3132+
return {true, nullptr};
30863133

30873134
auto p_it = p->template find_child<Direction>(parent);
30883135
if (p_it == p->template end<Direction>()) {
3089-
/* Detached leaf, need to start from the top. */
3090-
return fallback();
3136+
/* Detached leaf, cannot advance. */
3137+
return {false, nullptr};
30913138
}
30923139

3093-
return next_leaf<Direction>(p_it, p, k);
3140+
return next_leaf<Direction>(p_it, p);
30943141
}
30953142

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

31003147
auto leaf = find_leaf<Direction>(n);
31013148
if (!leaf)
3102-
return fallback();
3149+
return {false, nullptr};
31033150

3104-
return leaf;
3151+
return {true, leaf};
31053152
}
31063153
}
31073154

0 commit comments

Comments
 (0)