Bitcoin ABC  0.22.13
P2P Digital Currency
sync.h
Go to the documentation of this file.
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2016 The Bitcoin Core developers
3 // Distributed under the MIT software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5 
6 #ifndef BITCOIN_SYNC_H
7 #define BITCOIN_SYNC_H
8 
9 #include <threadsafety.h>
10 #include <util/macros.h>
11 
12 #include <condition_variable>
13 #include <mutex>
14 #include <string>
15 #include <thread>
16 
18 // //
19 // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
20 // //
22 
23 /*
24 RecursiveMutex mutex;
25  std::recursive_mutex mutex;
26 
27 LOCK(mutex);
28  std::unique_lock<std::recursive_mutex> criticalblock(mutex);
29 
30 LOCK2(mutex1, mutex2);
31  std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
32  std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
33 
34 TRY_LOCK(mutex, name);
35  std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
36 
37 ENTER_CRITICAL_SECTION(mutex); // no RAII
38  mutex.lock();
39 
40 LEAVE_CRITICAL_SECTION(mutex); // no RAII
41  mutex.unlock();
42  */
43 
45 // //
46 // THE ACTUAL IMPLEMENTATION //
47 // //
49 
50 #ifdef DEBUG_LOCKORDER
51 void EnterCritical(const char *pszName, const char *pszFile, int nLine,
52  void *cs, bool fTry = false);
53 void LeaveCritical();
54 void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
55  const char *file, int line);
56 std::string LocksHeld();
57 template <typename MutexType>
58 void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
59  MutexType *cs) ASSERT_EXCLUSIVE_LOCK(cs);
60 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
61  int nLine, void *cs);
62 void DeleteLock(void *cs);
63 
69 extern bool g_debug_lockorder_abort;
70 #else
71 static inline void EnterCritical(const char *pszName, const char *pszFile,
72  int nLine, void *cs, bool fTry = false) {}
73 static inline void LeaveCritical() {}
74 static inline void CheckLastCritical(void *cs, std::string &lockname,
75  const char *guardname, const char *file,
76  int line) {}
77 template <typename MutexType>
78 static inline void AssertLockHeldInternal(const char *pszName,
79  const char *pszFile, int nLine,
80  MutexType *cs)
82 static inline void AssertLockNotHeldInternal(const char *pszName,
83  const char *pszFile, int nLine,
84  void *cs) {}
85 static inline void DeleteLock(void *cs) {}
86 #endif
87 #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
88 #define AssertLockNotHeld(cs) \
89  AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
90 
95 template <typename PARENT> class LOCKABLE AnnotatedMixin : public PARENT {
96 public:
97  ~AnnotatedMixin() { DeleteLock((void *)this); }
98 
99  void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); }
100 
101  void unlock() UNLOCK_FUNCTION() { PARENT::unlock(); }
102 
104  return PARENT::try_lock();
105  }
106 
107  using UniqueLock = std::unique_lock<PARENT>;
108 };
109 
115 
118 
119 #ifdef DEBUG_LOCKCONTENTION
120 void PrintLockContention(const char *pszName, const char *pszFile, int nLine);
121 #endif
122 
124 template <typename Mutex, typename Base = typename Mutex::UniqueLock>
125 class SCOPED_LOCKABLE UniqueLock : public Base {
126 private:
127  void Enter(const char *pszName, const char *pszFile, int nLine) {
128  EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()));
129 #ifdef DEBUG_LOCKCONTENTION
130  if (!Base::try_lock()) {
131  PrintLockContention(pszName, pszFile, nLine);
132 #endif
133  Base::lock();
134 #ifdef DEBUG_LOCKCONTENTION
135  }
136 #endif
137  }
138 
139  bool TryEnter(const char *pszName, const char *pszFile, int nLine) {
140  EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()), true);
141  Base::try_lock();
142  if (!Base::owns_lock()) {
143  LeaveCritical();
144  }
145  return Base::owns_lock();
146  }
147 
148 public:
149  UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile,
150  int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn)
151  : Base(mutexIn, std::defer_lock) {
152  if (fTry) {
153  TryEnter(pszName, pszFile, nLine);
154  } else {
155  Enter(pszName, pszFile, nLine);
156  }
157  }
158 
159  UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile,
160  int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) {
161  if (!pmutexIn) {
162  return;
163  }
164 
165  *static_cast<Base *>(this) = Base(*pmutexIn, std::defer_lock);
166  if (fTry) {
167  TryEnter(pszName, pszFile, nLine);
168  } else {
169  Enter(pszName, pszFile, nLine);
170  }
171  }
172 
174  if (Base::owns_lock()) {
175  LeaveCritical();
176  }
177  }
178 
179  operator bool() { return Base::owns_lock(); }
180 
181 protected:
182  // needed for reverse_lock
184 
185 public:
190  class reverse_lock {
191  public:
192  explicit reverse_lock(UniqueLock &_lock, const char *_guardname,
193  const char *_file, int _line)
194  : lock(_lock), file(_file), line(_line) {
195  CheckLastCritical((void *)lock.mutex(), lockname, _guardname, _file,
196  _line);
197  lock.unlock();
198  LeaveCritical();
199  lock.swap(templock);
200  }
201 
203  templock.swap(lock);
204  EnterCritical(lockname.c_str(), file.c_str(), line,
205  (void *)lock.mutex());
206  lock.lock();
207  }
208 
209  private:
210  reverse_lock(reverse_lock const &);
211  reverse_lock &operator=(reverse_lock const &);
212 
215  std::string lockname;
216  const std::string file;
217  const int line;
218  };
219  friend class reverse_lock;
220 };
221 
222 #define REVERSE_LOCK(g) \
223  typename std::decay<decltype(g)>::type::reverse_lock PASTE2( \
224  revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
225 
226 template <typename MutexArg>
227 using DebugLock = UniqueLock<typename std::remove_reference<
228  typename std::remove_pointer<MutexArg>::type>::type>;
229 
230 #define LOCK(cs) \
231  DebugLock<decltype(cs)> PASTE2(criticalblock, \
232  __COUNTER__)(cs, #cs, __FILE__, __LINE__)
233 #define LOCK2(cs1, cs2) \
234  DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
235  DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
236 #define TRY_LOCK(cs, name) \
237  DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
238 #define WAIT_LOCK(cs, name) \
239  DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
240 
241 #define ENTER_CRITICAL_SECTION(cs) \
242  { \
243  EnterCritical(#cs, __FILE__, __LINE__, (void *)(&cs)); \
244  (cs).lock(); \
245  }
246 
247 #define LEAVE_CRITICAL_SECTION(cs) \
248  { \
249  (cs).unlock(); \
250  LeaveCritical(); \
251  }
252 
261 #define WITH_LOCK(cs, code) \
262  [&] { \
263  LOCK(cs); \
264  code; \
265  }()
266 
267 class CSemaphore {
268 private:
269  std::condition_variable condition;
270  std::mutex mutex;
271  int value;
272 
273 public:
274  explicit CSemaphore(int init) : value(init) {}
275 
276  void wait() {
277  std::unique_lock<std::mutex> lock(mutex);
278  condition.wait(lock, [&]() { return value >= 1; });
279  value--;
280  }
281 
282  bool try_wait() {
283  std::lock_guard<std::mutex> lock(mutex);
284  if (value < 1) {
285  return false;
286  }
287  value--;
288  return true;
289  }
290 
291  void post() {
292  {
293  std::lock_guard<std::mutex> lock(mutex);
294  value++;
295  }
296  condition.notify_one();
297  }
298 };
299 
302 private:
305 
306 public:
307  void Acquire() {
308  if (fHaveGrant) {
309  return;
310  }
311  sem->wait();
312  fHaveGrant = true;
313  }
314 
315  void Release() {
316  if (!fHaveGrant) {
317  return;
318  }
319  sem->post();
320  fHaveGrant = false;
321  }
322 
323  bool TryAcquire() {
324  if (!fHaveGrant && sem->try_wait()) {
325  fHaveGrant = true;
326  }
327  return fHaveGrant;
328  }
329 
330  void MoveTo(CSemaphoreGrant &grant) {
331  grant.Release();
332  grant.sem = sem;
333  grant.fHaveGrant = fHaveGrant;
334  fHaveGrant = false;
335  }
336 
337  CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
338 
339  explicit CSemaphoreGrant(CSemaphore &sema, bool fTry = false)
340  : sem(&sema), fHaveGrant(false) {
341  if (fTry) {
342  TryAcquire();
343  } else {
344  Acquire();
345  }
346  }
347 
348  ~CSemaphoreGrant() { Release(); }
349 
350  operator bool() const { return fHaveGrant; }
351 };
352 
353 // Utility class for indicating to compiler thread analysis that a mutex is
354 // locked (when it couldn't be determined otherwise).
356  template <typename Mutex>
357  explicit LockAssertion(Mutex &mutex) EXCLUSIVE_LOCK_FUNCTION(mutex) {
358 #ifdef DEBUG_LOCKORDER
359  AssertLockHeld(mutex);
360 #endif
361  }
363 };
364 
365 #endif // BITCOIN_SYNC_H
static void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, void *cs)
Definition: sync.h:82
void MoveTo(CSemaphoreGrant &grant)
Definition: sync.h:330
void unlock() UNLOCK_FUNCTION()
Definition: sync.h:101
#define EXCLUSIVE_LOCK_FUNCTION(...)
Definition: threadsafety.h:49
reverse_lock(UniqueLock &_lock, const char *_guardname, const char *_file, int _line)
Definition: sync.h:192
void Enter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:127
UniqueLock & lock
Definition: sync.h:213
static void pool cs
RAII-style semaphore lock.
Definition: sync.h:301
bool try_wait()
Definition: sync.h:282
static void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) ASSERT_EXCLUSIVE_LOCK(cs)
Definition: sync.h:78
UniqueLock templock
Definition: sync.h:214
~AnnotatedMixin()
Definition: sync.h:97
void lock() EXCLUSIVE_LOCK_FUNCTION()
Definition: sync.h:99
void Acquire()
Definition: sync.h:307
#define UNLOCK_FUNCTION(...)
Definition: threadsafety.h:53
CSemaphoreGrant(CSemaphore &sema, bool fTry=false)
Definition: sync.h:339
#define AssertLockHeld(cs)
Definition: sync.h:87
~CSemaphoreGrant()
Definition: sync.h:348
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
Definition: sync.h:103
static void DeleteLock(void *cs)
Definition: sync.h:85
UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
Definition: sync.h:159
CSemaphore * sem
Definition: sync.h:303
UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(mutexIn)
Definition: sync.h:149
int value
Definition: sync.h:271
~UniqueLock() UNLOCK_FUNCTION()
Definition: sync.h:173
void Release()
Definition: sync.h:315
static void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:74
static void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:71
static void LeaveCritical()
Definition: sync.h:73
CSemaphoreGrant()
Definition: sync.h:337
#define LOCKABLE
Definition: threadsafety.h:43
std::condition_variable condition
Definition: sync.h:269
Template mixin that adds -Wthread-safety locking annotations and lock order checking to a subset of t...
Definition: sync.h:95
const std::string file
Definition: sync.h:216
~LockAssertion() UNLOCK_FUNCTION()
Definition: sync.h:362
An RAII-style reverse lock.
Definition: sync.h:190
void wait()
Definition: sync.h:276
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
Definition: threadsafety.h:51
std::string lockname
Definition: sync.h:215
#define SCOPED_LOCKABLE
Definition: threadsafety.h:44
CSemaphore(int init)
Definition: sync.h:274
UniqueLock()
Definition: sync.h:183
LockAssertion(Mutex &mutex) EXCLUSIVE_LOCK_FUNCTION(mutex)
Definition: sync.h:357
Wrapper around std::unique_lock style lock for Mutex.
Definition: sync.h:125
AnnotatedMixin< std::mutex > Mutex
Wrapped mutex: supports waiting but not recursive locking.
Definition: sync.h:117
bool TryEnter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:139
void post()
Definition: sync.h:291
bool TryAcquire()
Definition: sync.h:323
bool fHaveGrant
Definition: sync.h:304
#define ASSERT_EXCLUSIVE_LOCK(...)
Definition: threadsafety.h:59
std::mutex mutex
Definition: sync.h:270