CatapultServer  v0.5.0.1 (Elephant)
SpinReaderWriterLock.h
Go to the documentation of this file.
1 
21 #pragma once
22 #include "catapult/exceptions.h"
23 #include "catapult/functions.h"
24 #include <atomic>
25 #include <thread>
26 
27 namespace catapult { namespace utils {
28 
35  template<typename TReaderNotificationPolicy>
36  class BasicSpinReaderWriterLock : private TReaderNotificationPolicy {
37  private:
38  // 0[active writer]|1234567[total writers]|01234567[total readers]
39  static constexpr uint16_t Active_Writer_Flag = 0x8000;
40  static constexpr uint16_t Pending_Writer_Mask = 0x7F00;
41  static constexpr uint16_t Reader_Mask = 0x00FF;
42  static constexpr uint16_t Writer_Mask = Pending_Writer_Mask | Active_Writer_Flag;
43 
44  static constexpr uint16_t Active_Reader_Increment = 0x0001;
45  static constexpr uint16_t Pending_Writer_Increment = 0x0100;
46 
47  private:
48 #pragma push_macro("Yield")
49 #undef Yield
50  static void Yield() {
51  std::this_thread::yield();
52  }
53 #pragma pop_macro("Yield")
54 
55  private:
57  class LockGuard {
58  protected:
59  explicit LockGuard(const action& resetFunc)
60  : m_resetFunc(resetFunc)
61  , m_isMoved(false)
62  {}
63 
65  if (m_isMoved)
66  return;
67 
68  m_resetFunc();
69  }
70 
71  public:
73  rhs.m_isMoved = true;
74  }
75 
76  private:
78  bool m_isMoved;
79  };
80 
81  public:
83  struct WriterLockGuard : public LockGuard {
84  public:
86  explicit WriterLockGuard(std::atomic<uint16_t>& value, bool& isActive)
87  : LockGuard([&value, &isActive]() {
88  // unset the active writer flag and change the writer to a reader
90  isActive = false;
91  })
92  {}
93 
95  WriterLockGuard(WriterLockGuard&&) = default;
96  };
97 
99  struct ReaderLockGuard : public LockGuard {
100  public:
102  explicit ReaderLockGuard(std::atomic<uint16_t>& value, TReaderNotificationPolicy& notificationPolicy)
103  : LockGuard([&value, &notificationPolicy]() {
104  // decrease the number of readers by one
105  value.fetch_sub(Active_Reader_Increment);
106  notificationPolicy.readerReleased();
107  })
108  , m_value(value)
109  , m_isWriterActive(false) {
110  notificationPolicy.readerAcquired();
111  }
112 
114  ReaderLockGuard(ReaderLockGuard&&) = default;
115 
116  public:
119 
120  // mark a pending write by changing the reader to a writer
122 
123  // wait for exclusive access (when there is no active writer and no readers)
124  uint16_t expected = m_value & Pending_Writer_Mask;
125  while (!m_value.compare_exchange_strong(expected, expected | Active_Writer_Flag)) {
126  Yield();
127  expected = m_value & Pending_Writer_Mask;
128  }
129 
131  }
132 
133  private:
135  if (m_isWriterActive)
136  CATAPULT_THROW_RUNTIME_ERROR("reader lock has already been promoted");
137 
138  m_isWriterActive = true;
139  }
140 
141  private:
142  std::atomic<uint16_t>& m_value;
144  };
145 
146  public:
149  {}
150 
151  public:
154  uint16_t current = m_value;
155  for (;;) {
156  // wait for any pending writes to complete
157  if (0 != (current & Pending_Writer_Mask)) {
158  Yield();
159  current = m_value;
160  continue;
161  }
162 
163  // try to increment the number of readers by one
164  uint16_t desired = current + Active_Reader_Increment;
165  if (m_value.compare_exchange_strong(current, desired))
166  break;
167 
168  Yield();
169  }
170 
171  return ReaderLockGuard(m_value, *this);
172  }
173 
174  public:
176  inline bool isWriterPending() const {
177  return isSet(Pending_Writer_Mask);
178  }
179 
181  inline bool isWriterActive() const {
182  return isSet(Active_Writer_Flag);
183  }
184 
186  inline bool isReaderActive() const {
187  return isSet(Reader_Mask);
188  }
189 
190  private:
191  inline bool isSet(uint16_t mask) const {
192  return 0 != (m_value & mask);
193  }
194 
195  private:
196  std::atomic<uint16_t> m_value;
197  };
198 
202  constexpr void readerAcquired()
203  {}
204 
206  constexpr void readerReleased()
207  {}
208  };
209 }}
210 
211 #ifdef ENABLE_CATAPULT_DIAGNOSTICS
213 #endif
214 
215 namespace catapult { namespace utils {
216 
217 #ifdef ENABLE_CATAPULT_DIAGNOSTICS
218  using DefaultReaderNotificationPolicy = ReentrancyCheckReaderNotificationPolicy;
219 #else
221 #endif
222 
225 }}
catapult::utils::BasicSpinReaderWriterLock::LockGuard::LockGuard
LockGuard(const action &resetFunc)
Definition: SpinReaderWriterLock.h:59
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard::markActiveWriter
void markActiveWriter()
Definition: SpinReaderWriterLock.h:134
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard::promoteToWriter
WriterLockGuard promoteToWriter()
Definition: SpinReaderWriterLock.h:117
catapult::utils::BasicSpinReaderWriterLock::Pending_Writer_Mask
static constexpr uint16_t Pending_Writer_Mask
Definition: SpinReaderWriterLock.h:40
exceptions.h
catapult::utils::BasicSpinReaderWriterLock::Active_Writer_Flag
static constexpr uint16_t Active_Writer_Flag
Definition: SpinReaderWriterLock.h:39
catapult::utils::BasicSpinReaderWriterLock::BasicSpinReaderWriterLock
BasicSpinReaderWriterLock()
Creates an unlocked lock.
Definition: SpinReaderWriterLock.h:148
ReentrancyCheckReaderNotificationPolicy.h
catapult::utils::BasicSpinReaderWriterLock::LockGuard::~LockGuard
~LockGuard()
Definition: SpinReaderWriterLock.h:64
catapult::utils::BasicSpinReaderWriterLock::Pending_Writer_Increment
static constexpr uint16_t Pending_Writer_Increment
Definition: SpinReaderWriterLock.h:45
catapult::utils::BasicSpinReaderWriterLock::Yield
static void Yield()
Definition: SpinReaderWriterLock.h:50
catapult::utils::BasicSpinReaderWriterLock::LockGuard::m_resetFunc
action m_resetFunc
Definition: SpinReaderWriterLock.h:77
functions.h
catapult::utils::DefaultReaderNotificationPolicy
NoOpReaderNotificationPolicy DefaultReaderNotificationPolicy
Definition: SpinReaderWriterLock.h:220
catapult::utils::BasicSpinReaderWriterLock::WriterLockGuard::WriterLockGuard
WriterLockGuard(std::atomic< uint16_t > &value, bool &isActive)
Creates a guard around value and isActive.
Definition: SpinReaderWriterLock.h:86
catapult::utils::BasicSpinReaderWriterLock::m_value
std::atomic< uint16_t > m_value
Definition: SpinReaderWriterLock.h:196
catapult::utils::BasicSpinReaderWriterLock::Active_Reader_Increment
static constexpr uint16_t Active_Reader_Increment
Definition: SpinReaderWriterLock.h:44
catapult::utils::BasicSpinReaderWriterLock::acquireReader
ReaderLockGuard acquireReader()
Blocks until a reader lock can be acquired.
Definition: SpinReaderWriterLock.h:153
catapult::utils::BasicSpinReaderWriterLock::LockGuard
Base class for RAII lock guards.
Definition: SpinReaderWriterLock.h:57
catapult::utils::BasicSpinReaderWriterLock::WriterLockGuard
A writer lock guard.
Definition: SpinReaderWriterLock.h:83
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard::ReaderLockGuard
ReaderLockGuard(std::atomic< uint16_t > &value, TReaderNotificationPolicy &notificationPolicy)
Creates a guard around value and notificationPolicy.
Definition: SpinReaderWriterLock.h:102
catapult::action
std::function< void()> action
An action function.
Definition: functions.h:27
catapult::utils::BasicSpinReaderWriterLock::isSet
bool isSet(uint16_t mask) const
Definition: SpinReaderWriterLock.h:191
CATAPULT_THROW_RUNTIME_ERROR
#define CATAPULT_THROW_RUNTIME_ERROR(MESSAGE)
Macro used to throw a catapult runtime error.
Definition: exceptions.h:167
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard
A reader lock guard.
Definition: SpinReaderWriterLock.h:99
catapult::utils::BasicSpinReaderWriterLock::Writer_Mask
static constexpr uint16_t Writer_Mask
Definition: SpinReaderWriterLock.h:42
catapult::utils::NoOpReaderNotificationPolicy::readerAcquired
constexpr void readerAcquired()
A reader was acquried by the current thread.
Definition: SpinReaderWriterLock.h:202
catapult::utils::BasicSpinReaderWriterLock::LockGuard::m_isMoved
bool m_isMoved
Definition: SpinReaderWriterLock.h:78
catapult::utils::NoOpReaderNotificationPolicy::readerReleased
constexpr void readerReleased()
A reader was released by the current thread.
Definition: SpinReaderWriterLock.h:206
catapult
Definition: AddressExtractionExtension.cpp:28
catapult::utils::BasicSpinReaderWriterLock::isWriterActive
bool isWriterActive() const
Returns true if there is an active writer.
Definition: SpinReaderWriterLock.h:181
catapult::utils::NoOpReaderNotificationPolicy
A no-op reader notification policy.
Definition: SpinReaderWriterLock.h:200
catapult::utils::BasicSpinReaderWriterLock::Reader_Mask
static constexpr uint16_t Reader_Mask
Definition: SpinReaderWriterLock.h:41
catapult::utils::BasicSpinReaderWriterLock::isReaderActive
bool isReaderActive() const
Returns true if there is an active reader.
Definition: SpinReaderWriterLock.h:186
catapult::utils::BasicSpinReaderWriterLock::isWriterPending
bool isWriterPending() const
Returns true if there is a pending (or active) writer.
Definition: SpinReaderWriterLock.h:176
catapult::utils::BasicSpinReaderWriterLock
Definition: SpinReaderWriterLock.h:36
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard::m_value
std::atomic< uint16_t > & m_value
Definition: SpinReaderWriterLock.h:142
catapult::utils::BasicSpinReaderWriterLock::LockGuard::LockGuard
LockGuard(LockGuard &&rhs)
Definition: SpinReaderWriterLock.h:72
catapult::utils::BasicSpinReaderWriterLock::ReaderLockGuard::m_isWriterActive
bool m_isWriterActive
Definition: SpinReaderWriterLock.h:143