Bitcoin ABC  0.26.3
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 #ifdef DEBUG_LOCKCONTENTION
10 #include <logging.h>
11 #include <logging/timer.h>
12 #endif
13 
14 #include <threadsafety.h>
15 #include <util/macros.h>
16 
17 #include <condition_variable>
18 #include <mutex>
19 #include <string>
20 #include <thread>
21 
23 // //
24 // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
25 // //
27 
28 /*
29 RecursiveMutex mutex;
30  std::recursive_mutex mutex;
31 
32 LOCK(mutex);
33  std::unique_lock<std::recursive_mutex> criticalblock(mutex);
34 
35 LOCK2(mutex1, mutex2);
36  std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
37  std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
38 
39 TRY_LOCK(mutex, name);
40  std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
41 
42 ENTER_CRITICAL_SECTION(mutex); // no RAII
43  mutex.lock();
44 
45 LEAVE_CRITICAL_SECTION(mutex); // no RAII
46  mutex.unlock();
47  */
48 
50 // //
51 // THE ACTUAL IMPLEMENTATION //
52 // //
54 
55 #ifdef DEBUG_LOCKORDER
56 void EnterCritical(const char *pszName, const char *pszFile, int nLine,
57  void *cs, bool fTry = false);
58 void LeaveCritical();
59 void CheckLastCritical(void *cs, std::string &lockname, const char *guardname,
60  const char *file, int line);
61 std::string LocksHeld();
62 template <typename MutexType>
63 void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine,
64  MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
65 template <typename MutexType>
66 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
67  int nLine, MutexType *cs) LOCKS_EXCLUDED(cs);
68 void DeleteLock(void *cs);
69 bool LockStackEmpty();
70 
76 extern bool g_debug_lockorder_abort;
77 #else
78 inline void EnterCritical(const char *pszName, const char *pszFile, int nLine,
79  void *cs, bool fTry = false) {}
80 inline void LeaveCritical() {}
81 inline void CheckLastCritical(void *cs, std::string &lockname,
82  const char *guardname, const char *file,
83  int line) {}
84 template <typename MutexType>
85 inline void AssertLockHeldInternal(const char *pszName, const char *pszFile,
86  int nLine, MutexType *cs)
88 template <typename MutexType>
89 void AssertLockNotHeldInternal(const char *pszName, const char *pszFile,
90  int nLine, MutexType *cs) LOCKS_EXCLUDED(cs) {}
91 inline void DeleteLock(void *cs) {}
92 inline bool LockStackEmpty() {
93  return true;
94 }
95 #endif
96 #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
97 #define AssertLockNotHeld(cs) \
98  AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
99 
104 template <typename PARENT> class LOCKABLE AnnotatedMixin : public PARENT {
105 public:
106  ~AnnotatedMixin() { DeleteLock((void *)this); }
107 
108  void lock() EXCLUSIVE_LOCK_FUNCTION() { PARENT::lock(); }
109 
110  void unlock() UNLOCK_FUNCTION() { PARENT::unlock(); }
111 
113  return PARENT::try_lock();
114  }
115 
116  using UniqueLock = std::unique_lock<PARENT>;
117 #ifdef __clang__
122  const AnnotatedMixin &operator!() const { return *this; }
123 #endif // __clang__
124 };
125 
131 
134 
136 template <typename Mutex, typename Base = typename Mutex::UniqueLock>
137 class SCOPED_LOCKABLE UniqueLock : public Base {
138 private:
139  void Enter(const char *pszName, const char *pszFile, int nLine) {
140  EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()));
141 #ifdef DEBUG_LOCKCONTENTION
142  if (Base::try_lock()) {
143  return;
144  }
146  strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine),
147  BCLog::LOCK);
148 #endif
149  Base::lock();
150  }
151 
152  bool TryEnter(const char *pszName, const char *pszFile, int nLine) {
153  EnterCritical(pszName, pszFile, nLine, (void *)(Base::mutex()), true);
154  Base::try_lock();
155  if (!Base::owns_lock()) {
156  LeaveCritical();
157  }
158  return Base::owns_lock();
159  }
160 
161 public:
162  UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile,
163  int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn)
164  : Base(mutexIn, std::defer_lock) {
165  if (fTry) {
166  TryEnter(pszName, pszFile, nLine);
167  } else {
168  Enter(pszName, pszFile, nLine);
169  }
170  }
171 
172  UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile,
173  int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) {
174  if (!pmutexIn) {
175  return;
176  }
177 
178  *static_cast<Base *>(this) = Base(*pmutexIn, std::defer_lock);
179  if (fTry) {
180  TryEnter(pszName, pszFile, nLine);
181  } else {
182  Enter(pszName, pszFile, nLine);
183  }
184  }
185 
187  if (Base::owns_lock()) {
188  LeaveCritical();
189  }
190  }
191 
192  operator bool() { return Base::owns_lock(); }
193 
194 protected:
195  // needed for reverse_lock
197 
198 public:
203  class reverse_lock {
204  public:
205  explicit reverse_lock(UniqueLock &_lock, const char *_guardname,
206  const char *_file, int _line)
207  : lock(_lock), file(_file), line(_line) {
208  CheckLastCritical((void *)lock.mutex(), lockname, _guardname, _file,
209  _line);
210  lock.unlock();
211  LeaveCritical();
212  lock.swap(templock);
213  }
214 
216  templock.swap(lock);
217  EnterCritical(lockname.c_str(), file.c_str(), line,
218  (void *)lock.mutex());
219  lock.lock();
220  }
221 
222  private:
225 
228  std::string lockname;
229  const std::string file;
230  const int line;
231  };
232  friend class reverse_lock;
233 };
234 
235 #define REVERSE_LOCK(g) \
236  typename std::decay<decltype(g)>::type::reverse_lock PASTE2( \
237  revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
238 
239 template <typename MutexArg>
240 using DebugLock = UniqueLock<typename std::remove_reference<
241  typename std::remove_pointer<MutexArg>::type>::type>;
242 
243 #define LOCK(cs) \
244  DebugLock<decltype(cs)> PASTE2(criticalblock, \
245  __COUNTER__)(cs, #cs, __FILE__, __LINE__)
246 #define LOCK2(cs1, cs2) \
247  DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
248  DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
249 #define TRY_LOCK(cs, name) \
250  DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
251 #define WAIT_LOCK(cs, name) \
252  DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
253 
254 #define ENTER_CRITICAL_SECTION(cs) \
255  { \
256  EnterCritical(#cs, __FILE__, __LINE__, (void *)(&cs)); \
257  (cs).lock(); \
258  }
259 
260 #define LEAVE_CRITICAL_SECTION(cs) \
261  { \
262  std::string lockname; \
263  CheckLastCritical((void *)(&cs), lockname, #cs, __FILE__, __LINE__); \
264  (cs).unlock(); \
265  LeaveCritical(); \
266  }
267 
276 #define WITH_LOCK(cs, code) \
277  [&] { \
278  LOCK(cs); \
279  code; \
280  }()
281 
282 class CSemaphore {
283 private:
284  std::condition_variable condition;
285  std::mutex mutex;
286  int value;
287 
288 public:
289  explicit CSemaphore(int init) : value(init) {}
290 
291  void wait() {
292  std::unique_lock<std::mutex> lock(mutex);
293  condition.wait(lock, [&]() { return value >= 1; });
294  value--;
295  }
296 
297  bool try_wait() {
298  std::lock_guard<std::mutex> lock(mutex);
299  if (value < 1) {
300  return false;
301  }
302  value--;
303  return true;
304  }
305 
306  void post() {
307  {
308  std::lock_guard<std::mutex> lock(mutex);
309  value++;
310  }
311  condition.notify_one();
312  }
313 };
314 
317 private:
320 
321 public:
322  void Acquire() {
323  if (fHaveGrant) {
324  return;
325  }
326  sem->wait();
327  fHaveGrant = true;
328  }
329 
330  void Release() {
331  if (!fHaveGrant) {
332  return;
333  }
334  sem->post();
335  fHaveGrant = false;
336  }
337 
338  bool TryAcquire() {
339  if (!fHaveGrant && sem->try_wait()) {
340  fHaveGrant = true;
341  }
342  return fHaveGrant;
343  }
344 
345  void MoveTo(CSemaphoreGrant &grant) {
346  grant.Release();
347  grant.sem = sem;
348  grant.fHaveGrant = fHaveGrant;
349  fHaveGrant = false;
350  }
351 
352  CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
353 
354  explicit CSemaphoreGrant(CSemaphore &sema, bool fTry = false)
355  : sem(&sema), fHaveGrant(false) {
356  if (fTry) {
357  TryAcquire();
358  } else {
359  Acquire();
360  }
361  }
362 
364 
365  operator bool() const { return fHaveGrant; }
366 };
367 
368 #endif // BITCOIN_SYNC_H
Template mixin that adds -Wthread-safety locking annotations and lock order checking to a subset of t...
Definition: sync.h:104
~AnnotatedMixin()
Definition: sync.h:106
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
Definition: sync.h:112
void unlock() UNLOCK_FUNCTION()
Definition: sync.h:110
void lock() EXCLUSIVE_LOCK_FUNCTION()
Definition: sync.h:108
RAII-style semaphore lock.
Definition: sync.h:316
bool fHaveGrant
Definition: sync.h:319
CSemaphoreGrant(CSemaphore &sema, bool fTry=false)
Definition: sync.h:354
CSemaphoreGrant()
Definition: sync.h:352
void Release()
Definition: sync.h:330
bool TryAcquire()
Definition: sync.h:338
~CSemaphoreGrant()
Definition: sync.h:363
void MoveTo(CSemaphoreGrant &grant)
Definition: sync.h:345
void Acquire()
Definition: sync.h:322
CSemaphore * sem
Definition: sync.h:318
void wait()
Definition: sync.h:291
int value
Definition: sync.h:286
std::mutex mutex
Definition: sync.h:285
bool try_wait()
Definition: sync.h:297
CSemaphore(int init)
Definition: sync.h:289
std::condition_variable condition
Definition: sync.h:284
void post()
Definition: sync.h:306
An RAII-style reverse lock.
Definition: sync.h:203
reverse_lock(reverse_lock const &)
reverse_lock & operator=(reverse_lock const &)
std::string lockname
Definition: sync.h:228
const std::string file
Definition: sync.h:229
UniqueLock templock
Definition: sync.h:227
UniqueLock & lock
Definition: sync.h:226
reverse_lock(UniqueLock &_lock, const char *_guardname, const char *_file, int _line)
Definition: sync.h:205
Wrapper around std::unique_lock style lock for Mutex.
Definition: sync.h:137
bool TryEnter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:152
~UniqueLock() UNLOCK_FUNCTION()
Definition: sync.h:186
UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(mutexIn)
Definition: sync.h:162
void Enter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:139
UniqueLock()
Definition: sync.h:196
UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
Definition: sync.h:172
static void pool cs
Definition: common.cpp:28
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: sync.h:85
void DeleteLock(void *cs)
Definition: sync.h:91
#define LOCK(cs)
Definition: sync.h:243
AnnotatedMixin< std::mutex > Mutex
Wrapped mutex: supports waiting but not recursive locking.
Definition: sync.h:133
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:81
void LeaveCritical()
Definition: sync.h:80
void EnterCritical(const char *pszName, const char *pszFile, int nLine, void *cs, bool fTry=false)
Definition: sync.h:78
bool LockStackEmpty()
Definition: sync.h:92
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)
Definition: sync.h:89
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:56
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
Definition: threadsafety.h:51
#define LOCKS_EXCLUDED(...)
Definition: threadsafety.h:55
#define EXCLUSIVE_LOCK_FUNCTION(...)
Definition: threadsafety.h:49
#define SCOPED_LOCKABLE
Definition: threadsafety.h:44
#define LOCKABLE
Definition: threadsafety.h:43
#define UNLOCK_FUNCTION(...)
Definition: threadsafety.h:53
#define LOG_TIME_MICROS_WITH_CATEGORY(end_msg, log_category)
Definition: timer.h:94
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1201