Bitcoin ABC  0.22.13
P2P Digital Currency
rcu.h
Go to the documentation of this file.
1 // Copyright (c) 2018-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_RCU_H
6 #define BITCOIN_RCU_H
7 
8 #include <boost/noncopyable.hpp>
9 
10 #include <atomic>
11 #include <cassert>
12 #include <cstdint>
13 #include <functional>
14 #include <map>
15 #include <ostream>
16 #include <type_traits>
17 #include <utility>
18 
19 class RCUInfos;
20 class RCUReadLock;
21 
22 class RCUInfos {
23  std::atomic<uint64_t> state;
24  std::atomic<RCUInfos *> next;
25 
26  std::map<uint64_t, std::function<void()>> cleanups;
27 
28  // The largest revision possible means unlocked.
29  static const uint64_t UNLOCKED = -uint64_t(1);
30 
31  RCUInfos();
32  ~RCUInfos();
33 
34  void readLock() {
35  assert(!isLocked());
36  state.store(revision.load());
37  }
38 
39  void readFree() {
40  assert(isLocked());
41  state.store(UNLOCKED);
42  }
43 
44  bool isLocked() const { return state.load() != UNLOCKED; }
45  void registerCleanup(const std::function<void()> &f) {
46  cleanups.emplace(++revision, f);
47  }
48 
49  void synchronize();
50  void runCleanups();
51  uint64_t hasSyncedTo(uint64_t cutoff = UNLOCKED);
52 
53  friend class RCULock;
54  friend struct RCUTest;
55 
56  static std::atomic<uint64_t> revision;
57  static thread_local RCUInfos infos;
58 };
59 
60 class RCULock : public boost::noncopyable {
62 
63  explicit RCULock(RCUInfos *infosIn) : infos(infosIn) { infos->readLock(); }
64  friend class RCUInfos;
65 
66 public:
67  RCULock() : RCULock(&RCUInfos::infos) {}
68  ~RCULock() { infos->readFree(); }
69 
70  static bool isLocked() { return RCUInfos::infos.isLocked(); }
71  static void registerCleanup(const std::function<void()> &f) {
73  }
74 
76 };
77 
78 template <typename T> class RCUPtr {
79  T *ptr;
80 
81  // Private construction, so factories have to be used.
82  explicit RCUPtr(T *ptrIn) : ptr(ptrIn) {}
83 
84 public:
85  RCUPtr() : ptr(nullptr) {}
86 
87  ~RCUPtr() {
88  if (ptr != nullptr) {
89  ptr->release();
90  }
91  }
92 
96  static RCUPtr acquire(T *&ptrIn) {
97  RCUPtr ret(ptrIn);
98  ptrIn = nullptr;
99  return ret;
100  }
101 
105  template <typename... Args> static RCUPtr make(Args &&... args) {
106  return RCUPtr(new T(std::forward<Args>(args)...));
107  }
108 
112  static RCUPtr copy(T *ptr) {
113  if (ptr != nullptr) {
114  ptr->acquire();
115  }
116 
117  return RCUPtr::acquire(ptr);
118  }
119 
123  RCUPtr(const RCUPtr &src) : ptr(src.ptr) {
124  if (ptr != nullptr) {
125  ptr->acquire();
126  }
127  }
128 
129  RCUPtr &operator=(const RCUPtr &rhs) {
130  RCUPtr tmp(rhs);
131  std::swap(ptr, tmp.ptr);
132  return *this;
133  }
134 
138  RCUPtr(RCUPtr &&src) : RCUPtr() { std::swap(ptr, src.ptr); }
140  std::swap(ptr, rhs.ptr);
141  return *this;
142  }
143 
147  T *get() { return ptr; }
148  const T *get() const { return ptr; }
149 
153  T *release() {
154  T *oldPtr = ptr;
155  ptr = nullptr;
156  return oldPtr;
157  }
158 
162  T *operator->() { return ptr; }
163  const T *operator->() const { return ptr; }
164 
165  T &operator*() { return *ptr; }
166  const T &operator*() const { return *ptr; }
167 
168  explicit operator bool() const { return ptr != nullptr; }
169 
173  friend bool operator==(const RCUPtr &lhs, const T *rhs) {
174  return lhs.get() == rhs;
175  }
176 
177  friend bool operator==(const RCUPtr &lhs, const RCUPtr &rhs) {
178  return lhs == rhs.get();
179  }
180 
181  friend bool operator!=(const RCUPtr &lhs, const T *rhs) {
182  return !(lhs == rhs);
183  }
184 
185  friend bool operator!=(const RCUPtr &lhs, const RCUPtr &rhs) {
186  return !(lhs == rhs);
187  }
188 
192  friend std::ostream &operator<<(std::ostream &stream, const RCUPtr &rhs) {
193  return stream << rhs.ptr;
194  }
195 };
196 
197 #define IMPLEMENT_RCU_REFCOUNT(T) \
198 private: \
199  mutable std::atomic<T> refcount{0}; \
200  \
201  void acquire() const { refcount++; } \
202  \
203  bool tryDecrement() const { \
204  T count = refcount.load(); \
205  while (count > 0) { \
206  if (refcount.compare_exchange_weak(count, count - 1)) { \
207  return true; \
208  } \
209  } \
210  \
211  return false; \
212  } \
213  \
214  void release() const { \
215  if (tryDecrement()) { \
216  return; \
217  } \
218  \
219  RCULock::registerCleanup([this] { \
220  if (tryDecrement()) { \
221  return; \
222  } \
223  \
224  delete this; \
225  }); \
226  } \
227  \
228  static_assert(std::is_integral<T>::value, "T must be an integral type."); \
229  static_assert(std::is_unsigned<T>::value, "T must be unsigned."); \
230  \
231  template <typename> friend class ::RCUPtr
232 
233 #endif // BITCOIN_RCU_H
void runCleanups()
Definition: rcu.cpp:197
const T * operator->() const
Definition: rcu.h:163
RCUPtr(RCUPtr &&src)
Move semantic.
Definition: rcu.h:138
static RCUPtr acquire(T *&ptrIn)
Acquire ownership of some pointer.
Definition: rcu.h:96
RCULock()
Definition: rcu.h:67
friend bool operator==(const RCUPtr &lhs, const T *rhs)
Equality checks.
Definition: rcu.h:173
RCULock(RCUInfos *infosIn)
Definition: rcu.h:63
static thread_local RCUInfos infos
Definition: rcu.h:57
friend bool operator!=(const RCUPtr &lhs, const T *rhs)
Definition: rcu.h:181
RCUInfos * infos
Definition: rcu.h:61
static bool isLocked()
Definition: rcu.h:70
RCUInfos()
Definition: rcu.cpp:107
T * get()
Get allows to access the undelying pointer.
Definition: rcu.h:147
Definition: rcu.h:22
uint64_t hasSyncedTo(uint64_t cutoff=UNLOCKED)
Definition: rcu.cpp:216
std::map< uint64_t, std::function< void()> > cleanups
Definition: rcu.h:26
T * operator->()
Operator overloading for convenience.
Definition: rcu.h:162
T * ptr
Definition: rcu.h:79
static std::atomic< uint64_t > revision
Definition: rcu.h:56
~RCULock()
Definition: rcu.h:68
RCUPtr(T *ptrIn)
Definition: rcu.h:82
static RCUPtr make(Args &&... args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:105
Definition: rcu.h:60
RCUPtr & operator=(RCUPtr &&rhs)
Definition: rcu.h:139
RCUPtr & operator=(const RCUPtr &rhs)
Definition: rcu.h:129
~RCUPtr()
Definition: rcu.h:87
friend std::ostream & operator<<(std::ostream &stream, const RCUPtr &rhs)
ostream support.
Definition: rcu.h:192
friend bool operator!=(const RCUPtr &lhs, const RCUPtr &rhs)
Definition: rcu.h:185
friend struct RCUTest
Definition: rcu.h:54
friend bool operator==(const RCUPtr &lhs, const RCUPtr &rhs)
Definition: rcu.h:177
RCUPtr()
Definition: rcu.h:85
const T & operator*() const
Definition: rcu.h:166
static void registerCleanup(const std::function< void()> &f)
Definition: rcu.h:71
static void synchronize()
Definition: rcu.h:75
T * release()
Release transfers ownership of the pointer from RCUPtr to the caller.
Definition: rcu.h:153
T & operator*()
Definition: rcu.h:165
RCUPtr(const RCUPtr &src)
Copy semantic.
Definition: rcu.h:123
bool isLocked() const
Definition: rcu.h:44
static RCUPtr copy(T *ptr)
Construct a new RCUPtr without transferring owership.
Definition: rcu.h:112
void readLock()
Definition: rcu.h:34
void registerCleanup(const std::function< void()> &f)
Definition: rcu.h:45
void synchronize()
Definition: rcu.cpp:169
Definition: rcu.h:78
~RCUInfos()
Definition: rcu.cpp:117
std::atomic< RCUInfos * > next
Definition: rcu.h:24
static const uint64_t UNLOCKED
Definition: rcu.h:29
std::atomic< uint64_t > state
Definition: rcu.h:23
void readFree()
Definition: rcu.h:39