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