Bitcoin ABC  0.26.3
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 <atomic>
9 #include <cassert>
10 #include <cstdint>
11 #include <functional>
12 #include <map>
13 #include <ostream>
14 #include <type_traits>
15 #include <utility>
16 
17 class RCUInfos;
18 class RCUReadLock;
19 
20 class RCUInfos {
21  std::atomic<uint64_t> state;
22  std::atomic<RCUInfos *> next;
23 
24  bool isCleaningUp = false;
25  class RCUCleanupGuard;
26 
27  std::map<uint64_t, std::function<void()>> cleanups;
28 
29  // The largest revision possible means unlocked.
30  static const uint64_t UNLOCKED = -uint64_t(1);
31 
32  RCUInfos();
33  ~RCUInfos();
34 
35  void readLock() {
36  assert(!isLocked());
37  state.store(revision.load());
38  }
39 
40  void readFree() {
41  assert(isLocked());
42  state.store(UNLOCKED);
43  }
44 
45  bool isLocked() const { return state.load() != UNLOCKED; }
46  void registerCleanup(const std::function<void()> &f) {
47  cleanups.emplace(++revision, f);
48  }
49 
50  void synchronize();
51  void runCleanups();
52  uint64_t hasSyncedTo(uint64_t cutoff = UNLOCKED);
53 
54  friend class RCULock;
55  friend struct RCUTest;
56 
57  static std::atomic<uint64_t> revision;
58  static thread_local RCUInfos infos;
59 };
60 
61 class RCULock {
63 
64  explicit RCULock(RCUInfos *infosIn) : infos(infosIn) { infos->readLock(); }
65  friend class RCUInfos;
66 
67 public:
70  infos->readFree();
71  infos->runCleanups();
72  }
73 
74  RCULock(const RCULock &) = delete;
75  RCULock &operator=(const RCULock &) = delete;
76 
77  static bool isLocked() { return RCUInfos::infos.isLocked(); }
78  static void registerCleanup(const std::function<void()> &f) {
80  }
81 
83 };
84 
85 template <typename T> class RCUPtr {
86  T *ptr;
87 
88  // Private construction, so factories have to be used.
89  explicit RCUPtr(T *ptrIn) : ptr(ptrIn) {}
90 
91 public:
92  RCUPtr() : ptr(nullptr) {}
93 
94  ~RCUPtr() {
95  if (ptr != nullptr) {
96  ptr->decrementRefCount();
97  }
98  }
99 
103  static RCUPtr acquire(T *&ptrIn) {
104  RCUPtr ret(ptrIn);
105  ptrIn = nullptr;
106  return ret;
107  }
108 
112  template <typename... Args> static RCUPtr make(Args &&...args) {
113  return RCUPtr(new T(std::forward<Args>(args)...));
114  }
115 
119  static RCUPtr copy(T *ptr) {
120  if (ptr != nullptr) {
121  ptr->incrementRefCount();
122  }
123 
124  return RCUPtr::acquire(ptr);
125  }
126 
130  RCUPtr(const RCUPtr &src) : ptr(src.ptr) {
131  if (ptr != nullptr) {
132  ptr->incrementRefCount();
133  }
134  }
135 
136  RCUPtr &operator=(const RCUPtr &rhs) {
137  RCUPtr tmp(rhs);
138  std::swap(ptr, tmp.ptr);
139  return *this;
140  }
141 
146  template <typename U> RCUPtr(const RCUPtr<U> &src) : ptr(src.get()) {
147  if (ptr != nullptr) {
148  ptr->incrementRefCount();
149  }
150  }
151 
152  template <typename U> RCUPtr &operator=(const RCUPtr<U> &rhs) {
153  RCUPtr tmp(rhs);
154  std::swap(ptr, tmp.ptr);
155  return *this;
156  }
157 
161  RCUPtr(RCUPtr &&src) : RCUPtr() { std::swap(ptr, src.ptr); }
163  std::swap(ptr, rhs.ptr);
164  return *this;
165  }
166 
170  T *get() { return ptr; }
171  const T *get() const { return ptr; }
172 
176  T *release() {
177  T *oldPtr = ptr;
178  ptr = nullptr;
179  return oldPtr;
180  }
181 
185  T *operator->() { return ptr; }
186  const T *operator->() const { return ptr; }
187 
188  T &operator*() { return *ptr; }
189  const T &operator*() const { return *ptr; }
190 
191  explicit operator bool() const { return ptr != nullptr; }
192 
196  friend bool operator==(const RCUPtr &lhs, const T *rhs) {
197  return lhs.get() == rhs;
198  }
199 
200  friend bool operator==(const RCUPtr &lhs, const RCUPtr &rhs) {
201  return lhs == rhs.get();
202  }
203 
204  friend bool operator!=(const RCUPtr &lhs, const T *rhs) {
205  return !(lhs == rhs);
206  }
207 
208  friend bool operator!=(const RCUPtr &lhs, const RCUPtr &rhs) {
209  return !(lhs == rhs);
210  }
211 
215  friend std::ostream &operator<<(std::ostream &stream, const RCUPtr &rhs) {
216  return stream << rhs.ptr;
217  }
218 };
219 
220 #define IMPLEMENT_RCU_REFCOUNT(T) \
221 private: \
222  mutable std::atomic<T> refcount{0}; \
223  \
224  void incrementRefCount() const { refcount++; } \
225  \
226  bool tryDecrement() const { \
227  T count = refcount.load(); \
228  while (count > 0) { \
229  if (refcount.compare_exchange_weak(count, count - 1)) { \
230  return true; \
231  } \
232  } \
233  \
234  return false; \
235  } \
236  \
237  void decrementRefCount() const { \
238  if (tryDecrement()) { \
239  return; \
240  } \
241  \
242  RCULock::registerCleanup([this] { \
243  if (tryDecrement()) { \
244  return; \
245  } \
246  \
247  delete this; \
248  }); \
249  } \
250  \
251  static_assert(std::is_integral<T>::value, "T must be an integral type."); \
252  static_assert(std::is_unsigned<T>::value, "T must be unsigned."); \
253  \
254  template <typename> friend class ::RCUPtr
255 
257 namespace std {
258 template <typename T> struct hash<RCUPtr<T>> {
259  size_t operator()(const RCUPtr<T> &ptr) const noexcept {
260  return hash<decltype(ptr.get())>()(ptr.get());
261  }
262 };
263 } // end namespace std
264 
265 #endif // BITCOIN_RCU_H
Definition: rcu.h:20
bool isLocked() const
Definition: rcu.h:45
uint64_t hasSyncedTo(uint64_t cutoff=UNLOCKED)
Definition: rcu.cpp:235
std::atomic< RCUInfos * > next
Definition: rcu.h:22
void runCleanups()
Definition: rcu.cpp:208
~RCUInfos()
Definition: rcu.cpp:117
static const uint64_t UNLOCKED
Definition: rcu.h:30
static thread_local RCUInfos infos
Definition: rcu.h:58
std::map< uint64_t, std::function< void()> > cleanups
Definition: rcu.h:25
std::atomic< uint64_t > state
Definition: rcu.h:21
void readFree()
Definition: rcu.h:40
friend struct RCUTest
Definition: rcu.h:55
static std::atomic< uint64_t > revision
Definition: rcu.h:57
void registerCleanup(const std::function< void()> &f)
Definition: rcu.h:46
void synchronize()
Definition: rcu.cpp:169
bool isCleaningUp
Definition: rcu.h:24
RCUInfos()
Definition: rcu.cpp:107
void readLock()
Definition: rcu.h:35
Definition: rcu.h:61
static void registerCleanup(const std::function< void()> &f)
Definition: rcu.h:78
RCUInfos * infos
Definition: rcu.h:62
RCULock()
Definition: rcu.h:68
static bool isLocked()
Definition: rcu.h:77
RCULock(RCUInfos *infosIn)
Definition: rcu.h:64
static void synchronize()
Definition: rcu.h:82
RCULock & operator=(const RCULock &)=delete
RCULock(const RCULock &)=delete
~RCULock()
Definition: rcu.h:69
Definition: rcu.h:85
RCUPtr(RCUPtr &&src)
Move semantic.
Definition: rcu.h:161
const T * get() const
Definition: rcu.h:171
friend bool operator!=(const RCUPtr &lhs, const RCUPtr &rhs)
Definition: rcu.h:208
friend bool operator!=(const RCUPtr &lhs, const T *rhs)
Definition: rcu.h:204
RCUPtr & operator=(const RCUPtr< U > &rhs)
Definition: rcu.h:152
~RCUPtr()
Definition: rcu.h:94
const T * operator->() const
Definition: rcu.h:186
friend bool operator==(const RCUPtr &lhs, const RCUPtr &rhs)
Definition: rcu.h:200
const T & operator*() const
Definition: rcu.h:189
T * ptr
Definition: rcu.h:86
RCUPtr(T *ptrIn)
Definition: rcu.h:89
RCUPtr(const RCUPtr< U > &src)
Implicit converting constructor from RCUPtr to RCUPtr<T>, where U * is implicitely convertible to T *...
Definition: rcu.h:146
static RCUPtr acquire(T *&ptrIn)
Acquire ownership of some pointer.
Definition: rcu.h:103
static RCUPtr make(Args &&...args)
Construct a new object that is owned by the pointer.
Definition: rcu.h:112
RCUPtr & operator=(const RCUPtr &rhs)
Definition: rcu.h:136
RCUPtr()
Definition: rcu.h:92
static RCUPtr copy(T *ptr)
Construct a new RCUPtr without transferring owership.
Definition: rcu.h:119
RCUPtr(const RCUPtr &src)
Copy semantic.
Definition: rcu.h:130
friend bool operator==(const RCUPtr &lhs, const T *rhs)
Equality checks.
Definition: rcu.h:196
friend std::ostream & operator<<(std::ostream &stream, const RCUPtr &rhs)
ostream support.
Definition: rcu.h:215
T & operator*()
Definition: rcu.h:188
T * get()
Get allows to access the undelying pointer.
Definition: rcu.h:170
RCUPtr & operator=(RCUPtr &&rhs)
Definition: rcu.h:162
T * operator->()
Operator overloading for convenience.
Definition: rcu.h:185
T * release()
Release transfers ownership of the pointer from RCUPtr to the caller.
Definition: rcu.h:176
Implement std::hash so RCUPtr can be used as a key for maps or sets.
Definition: rcu.h:257
size_t operator()(const RCUPtr< T > &ptr) const noexcept
Definition: rcu.h:259
assert(!tx.IsCoinBase())