Bitcoin ABC  0.26.3
P2P Digital Currency
radix.h
Go to the documentation of this file.
1 // Copyright (c) 2019 The Bitcoin developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 
5 #ifndef BITCOIN_RADIX_H
6 #define BITCOIN_RADIX_H
7 
8 #include <rcu.h>
9 #include <util/system.h>
10 
11 #include <array>
12 #include <atomic>
13 #include <cstdint>
14 #include <memory>
15 #include <type_traits>
16 
17 template <typename T> struct PassthroughAdapter {
18  auto &&getId(const T &e) const { return e.getId(); }
19 };
20 
39 template <typename T, typename Adapter = PassthroughAdapter<T>>
40 struct RadixTree : private Adapter {
41 private:
42  static const int BITS = 4;
43  static const int MASK = (1 << BITS) - 1;
44  static const size_t CHILD_PER_LEVEL = 1 << BITS;
45 
46  using KeyType =
47  typename std::remove_reference<decltype(std::declval<Adapter &>().getId(
48  std::declval<T &>()))>::type;
49  static const size_t KEY_BITS = 8 * sizeof(KeyType);
50  static const uint32_t TOP_LEVEL = (KEY_BITS - 1) / BITS;
51 
52  struct RadixElement;
53  struct RadixNode;
54 
55  std::atomic<RadixElement> root;
56 
57 public:
59  ~RadixTree() { root.load().decrementRefCount(); }
60 
64  RadixTree(const RadixTree &src) : RadixTree() {
65  {
66  RCULock lock;
67  RadixElement e = src.root.load();
69  root = e;
70  }
71 
72  // Make sure we the writes in the tree are behind us so
73  // this copy won't mutate behind our back.
75  }
76 
77  RadixTree &operator=(const RadixTree &rhs) {
78  {
79  RCULock lock;
80  RadixElement e = rhs.root.load();
82  root.load().decrementRefCount();
83  root = e;
84  }
85 
86  // Make sure we the writes in the tree are behind us so
87  // this copy won't mutate behind our back.
89 
90  return *this;
91  }
92 
96  RadixTree(RadixTree &&src) : RadixTree() { *this = std::move(src); }
98  {
99  RCULock lock;
100  RadixElement e = rhs.root.load();
101  rhs.root = root.load();
102  root = e;
103  }
104 
105  return *this;
106  }
107 
112  bool insert(const RCUPtr<T> &value) { return insert(getId(*value), value); }
113 
118  RCUPtr<T> get(const KeyType &key) {
119  uint32_t level = TOP_LEVEL;
120 
121  RCULock lock;
122  RadixElement e = root.load();
123 
124  // Find a leaf.
125  while (e.isNode()) {
126  e = e.getNode()->get(level--, key)->load();
127  }
128 
129  T *leaf = e.getLeaf();
130  if (leaf == nullptr || getId(*leaf) != key) {
131  // We failed to find the proper element.
132  return RCUPtr<T>();
133  }
134 
135  // The leaf is non-null and the keys match. We have our guy.
136  return RCUPtr<T>::copy(leaf);
137  }
138 
139  RCUPtr<const T> get(const KeyType &key) const {
140  T const *ptr = const_cast<RadixTree *>(this)->get(key).release();
141  return RCUPtr<const T>::acquire(ptr);
142  }
143 
144  template <typename Callable> bool forEachLeaf(Callable &&func) const {
145  RCULock lock;
146  return forEachLeaf(root.load(), std::move(func));
147  }
148 
149 #define SEEK_LEAF_LOOP() \
150  RadixElement e = eptr->load(); \
151  \
152  /* Walk down the tree until we find a leaf for our node. */ \
153  do { \
154  while (e.isNode()) { \
155  Node: \
156  auto nptr = e.getNode(); \
157  if (!nptr->isShared()) { \
158  eptr = nptr->get(level--, key); \
159  e = eptr->load(); \
160  continue; \
161  } \
162  \
163  auto copy = std::make_unique<RadixNode>(*nptr); \
164  if (!eptr->compare_exchange_strong(e, RadixElement(copy.get()))) { \
165  /* We failed to insert our subtree, just try again. */ \
166  continue; \
167  } \
168  \
169  /* We have a subtree, resume normal operations from there. */ \
170  e.decrementRefCount(); \
171  eptr = copy->get(level--, key); \
172  e = eptr->load(); \
173  copy.release(); \
174  } \
175  } while (0)
176 
181  RCUPtr<T> remove(const KeyType &key) {
182  uint32_t level = TOP_LEVEL;
183 
184  RCULock lock;
185  std::atomic<RadixElement> *eptr = &root;
186 
187  SEEK_LEAF_LOOP();
188 
189  T *leaf = e.getLeaf();
190  if (leaf == nullptr || getId(*leaf) != key) {
191  // We failed to find the proper element.
192  return RCUPtr<T>();
193  }
194 
195  // We have the proper element, try to delete it.
196  if (eptr->compare_exchange_strong(e, RadixElement())) {
197  return RCUPtr<T>::acquire(leaf);
198  }
199 
200  // The element was replaced, either by a subtree or another element.
201  if (e.isNode()) {
202  goto Node;
203  }
204 
205  // The element in the slot is not the one we are looking for.
206  return RCUPtr<T>();
207  }
208 
209 private:
210  KeyType getId(const T &value) const { return Adapter::getId(value); }
211 
212  bool insert(const KeyType &key, RCUPtr<T> value) {
213  uint32_t level = TOP_LEVEL;
214 
215  RCULock lock;
216  std::atomic<RadixElement> *eptr = &root;
217 
218  while (true) {
219  SEEK_LEAF_LOOP();
220 
221  // If the slot is empty, try to insert right there.
222  if (e.getLeaf() == nullptr) {
223  if (eptr->compare_exchange_strong(e,
224  RadixElement(value.get()))) {
225  value.release();
226  return true;
227  }
228 
229  // CAS failed, we may have a node in there now.
230  if (e.isNode()) {
231  goto Node;
232  }
233  }
234 
235  // The element was already in the tree.
236  const KeyType &leafKey = getId(*e.getLeaf());
237  if (key == leafKey) {
238  return false;
239  }
240 
241  // There is an element there, but it isn't a subtree. We need to
242  // convert it into a subtree and resume insertion into that subtree.
243  auto newChild = std::make_unique<RadixNode>(level, leafKey, e);
244  if (eptr->compare_exchange_strong(e,
245  RadixElement(newChild.get()))) {
246  // We have a subtree, resume normal operations from there.
247  newChild.release();
248  } else {
249  // We failed to insert our subtree, clean it before it is freed.
250  newChild->get(level, leafKey)->store(RadixElement());
251  }
252  }
253  }
254 
255 #undef SEEK_LEAF_LOOP
256 
257  template <typename Callable>
258  bool forEachLeaf(RadixElement e, Callable &&func) const {
259  if (e.isLeaf()) {
260  T *leaf = e.getLeaf();
261  if (leaf != nullptr) {
262  return func(RCUPtr<T>::copy(leaf));
263  }
264 
265  return true;
266  }
267 
268  return e.getNode()->forEachChild(
269  [&](const std::atomic<RadixElement> *pElement) {
270  return forEachLeaf(pElement->load(), func);
271  });
272  }
273 
274  struct RadixElement {
275  private:
276  union {
278  T *leaf;
279  uintptr_t raw;
280  };
281 
282  static const uintptr_t DISCRIMINANT = 0x01;
283  bool getDiscriminant() const { return (raw & DISCRIMINANT) != 0; }
284 
285  public:
286  explicit RadixElement() noexcept : raw(DISCRIMINANT) {}
287  explicit RadixElement(RadixNode *nodeIn) noexcept : node(nodeIn) {}
288  explicit RadixElement(T *leafIn) noexcept : leaf(leafIn) {
289  raw |= DISCRIMINANT;
290  }
291 
297  if (isNode()) {
299  } else {
301  }
302  }
303 
305  if (isNode()) {
306  RadixNode *ptr = getNode();
308  } else {
309  T *ptr = getLeaf();
310  RCUPtr<T>::acquire(ptr);
311  }
312  }
313 
317  bool isNode() const { return !getDiscriminant(); }
318 
320  assert(isNode());
321  return node;
322  }
323 
324  const RadixNode *getNode() const {
325  assert(isNode());
326  return node;
327  }
328 
332  bool isLeaf() const { return getDiscriminant(); }
333 
334  T *getLeaf() {
335  assert(isLeaf());
336  return reinterpret_cast<T *>(raw & ~DISCRIMINANT);
337  }
338 
339  const T *getLeaf() const {
340  assert(isLeaf());
341  return const_cast<RadixElement *>(this)->getLeaf();
342  }
343  };
344 
345  struct RadixNode {
347 
348  private:
349  union {
350  std::array<std::atomic<RadixElement>, CHILD_PER_LEVEL> children;
351  std::array<RadixElement, CHILD_PER_LEVEL>
353  };
354 
355  public:
356  RadixNode(uint32_t level, const KeyType &key, RadixElement e)
358  get(level, key)->store(e);
359  }
360 
363  e.decrementRefCount();
364  }
365  }
366 
368  for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
369  auto e = rhs.children[i].load();
370  e.incrementRefCount();
372  }
373  }
374 
375  RadixNode &operator=(const RadixNode &) = delete;
376 
377  std::atomic<RadixElement> *get(uint32_t level, const KeyType &key) {
378  return &children[(key >> uint32_t(level * BITS)) & MASK];
379  }
380 
381  bool isShared() const { return refcount > 0; }
382 
383  template <typename Callable> bool forEachChild(Callable &&func) const {
384  for (size_t i = 0; i < CHILD_PER_LEVEL; i++) {
385  if (!func(&children[i])) {
386  return false;
387  }
388  }
389  return true;
390  }
391  };
392 
393  // Make sure the alignment works for T and RadixElement.
394  static_assert(alignof(T) > 1, "T's alignment must be 2 or more.");
395  static_assert(alignof(RadixNode) > 1,
396  "RadixNode alignment must be 2 or more.");
397 };
398 
399 #endif // BITCOIN_RADIX_H
Definition: rcu.h:61
static void synchronize()
Definition: rcu.h:82
Definition: rcu.h:85
static RCUPtr acquire(T *&ptrIn)
Acquire ownership of some pointer.
Definition: rcu.h:103
static RCUPtr copy(T *ptr)
Construct a new RCUPtr without transferring owership.
Definition: rcu.h:119
T * get()
Get allows to access the undelying pointer.
Definition: rcu.h:170
T * release()
Release transfers ownership of the pointer from RCUPtr to the caller.
Definition: rcu.h:176
#define SEEK_LEAF_LOOP()
Definition: radix.h:149
auto && getId(const T &e) const
Definition: radix.h:18
const RadixNode * getNode() const
Definition: radix.h:324
bool getDiscriminant() const
Definition: radix.h:283
bool isNode() const
Node features.
Definition: radix.h:317
static const uintptr_t DISCRIMINANT
Definition: radix.h:282
RadixNode * getNode()
Definition: radix.h:319
bool isLeaf() const
Leaf features.
Definition: radix.h:332
RadixElement(RadixNode *nodeIn) noexcept
Definition: radix.h:287
const T * getLeaf() const
Definition: radix.h:339
void incrementRefCount()
RadixElement is designed to be a dumb wrapper.
Definition: radix.h:296
void decrementRefCount()
Definition: radix.h:304
RadixNode * node
Definition: radix.h:277
RadixElement(T *leafIn) noexcept
Definition: radix.h:288
RadixElement() noexcept
Definition: radix.h:286
bool forEachChild(Callable &&func) const
Definition: radix.h:383
std::atomic< RadixElement > * get(uint32_t level, const KeyType &key)
Definition: radix.h:377
RadixNode & operator=(const RadixNode &)=delete
RadixNode(uint32_t level, const KeyType &key, RadixElement e)
Definition: radix.h:356
std::array< RadixElement, CHILD_PER_LEVEL > non_atomic_children_DO_NOT_USE
Definition: radix.h:352
bool isShared() const
Definition: radix.h:381
RadixNode(const RadixNode &rhs)
Definition: radix.h:367
std::array< std::atomic< RadixElement >, CHILD_PER_LEVEL > children
Definition: radix.h:350
IMPLEMENT_RCU_REFCOUNT(uint64_t)
This is a radix tree storing values identified by a unique key.
Definition: radix.h:40
static const int BITS
Definition: radix.h:42
static const uint32_t TOP_LEVEL
Definition: radix.h:50
RCUPtr< T > get(const KeyType &key)
Get the value corresponding to a key.
Definition: radix.h:118
RadixTree()
Definition: radix.h:58
RadixTree & operator=(const RadixTree &rhs)
Definition: radix.h:77
KeyType getId(const T &value) const
Definition: radix.h:210
static const size_t CHILD_PER_LEVEL
Definition: radix.h:44
RadixTree & operator=(RadixTree &&rhs)
Definition: radix.h:97
~RadixTree()
Definition: radix.h:59
RadixTree(RadixTree &&src)
Move semantic.
Definition: radix.h:96
typename std::remove_reference< decltype(std::declval< Adapter & >().getId(std::declval< T & >()))>::type KeyType
Definition: radix.h:48
bool forEachLeaf(RadixElement e, Callable &&func) const
Definition: radix.h:258
static const size_t KEY_BITS
Definition: radix.h:49
static const int MASK
Definition: radix.h:43
std::atomic< RadixElement > root
Definition: radix.h:53
RadixTree(const RadixTree &src)
Copy semantic.
Definition: radix.h:64
RCUPtr< const T > get(const KeyType &key) const
Definition: radix.h:139
RCUPtr< T > remove(const KeyType &key)
Remove an element from the tree.
Definition: radix.h:181
bool insert(const KeyType &key, RCUPtr< T > value)
Definition: radix.h:212
bool forEachLeaf(Callable &&func) const
Definition: radix.h:144
bool insert(const RCUPtr< T > &value)
Insert a value into the tree.
Definition: radix.h:112
assert(!tx.IsCoinBase())