Bitcoin Core 22.99.0
P2P Digital Currency
sync.h
Go to the documentation of this file.
1// Copyright (c) 2009-2010 Satoshi Nakamoto
2// Copyright (c) 2009-2020 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 <logging.h>
10#include <logging/timer.h>
11#include <threadsafety.h>
12#include <util/macros.h>
13
14#include <condition_variable>
15#include <mutex>
16#include <string>
17#include <thread>
18
20// //
21// THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
22// //
24
25/*
26RecursiveMutex mutex;
27 std::recursive_mutex mutex;
28
29LOCK(mutex);
30 std::unique_lock<std::recursive_mutex> criticalblock(mutex);
31
32LOCK2(mutex1, mutex2);
33 std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
34 std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
35
36TRY_LOCK(mutex, name);
37 std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
38
39ENTER_CRITICAL_SECTION(mutex); // no RAII
40 mutex.lock();
41
42LEAVE_CRITICAL_SECTION(mutex); // no RAII
43 mutex.unlock();
44 */
45
47// //
48// THE ACTUAL IMPLEMENTATION //
49// //
51
52#ifdef DEBUG_LOCKORDER
53template <typename MutexType>
54void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false);
55void LeaveCritical();
56void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
57std::string LocksHeld();
58template <typename MutexType>
59void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
60template <typename MutexType>
61void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
62void DeleteLock(void* cs);
63bool LockStackEmpty();
64
70extern bool g_debug_lockorder_abort;
71#else
72template <typename MutexType>
73inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
74inline void LeaveCritical() {}
75inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
76template <typename MutexType>
77inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
78template <typename MutexType>
79void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
80inline void DeleteLock(void* cs) {}
81inline bool LockStackEmpty() { return true; }
82#endif
83#define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
84#define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
85
90template <typename PARENT>
91class LOCKABLE AnnotatedMixin : public PARENT
92{
93public:
95 DeleteLock((void*)this);
96 }
97
99 {
100 PARENT::lock();
101 }
102
104 {
105 PARENT::unlock();
106 }
107
109 {
110 return PARENT::try_lock();
111 }
112
113 using UniqueLock = std::unique_lock<PARENT>;
114#ifdef __clang__
118 const AnnotatedMixin& operator!() const { return *this; }
119#endif // __clang__
120};
121
127
130
132template <typename Mutex, typename Base = typename Mutex::UniqueLock>
133class SCOPED_LOCKABLE UniqueLock : public Base
134{
135private:
136 void Enter(const char* pszName, const char* pszFile, int nLine)
137 {
138 EnterCritical(pszName, pszFile, nLine, Base::mutex());
139 if (Base::try_lock()) return;
140 LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
141 Base::lock();
142 }
143
144 bool TryEnter(const char* pszName, const char* pszFile, int nLine)
145 {
146 EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
147 Base::try_lock();
148 if (!Base::owns_lock()) {
150 }
151 return Base::owns_lock();
152 }
153
154public:
155 UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
156 {
157 if (fTry)
158 TryEnter(pszName, pszFile, nLine);
159 else
160 Enter(pszName, pszFile, nLine);
161 }
162
163 UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
164 {
165 if (!pmutexIn) return;
166
167 *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
168 if (fTry)
169 TryEnter(pszName, pszFile, nLine);
170 else
171 Enter(pszName, pszFile, nLine);
172 }
173
175 {
176 if (Base::owns_lock())
178 }
179
180 operator bool()
181 {
182 return Base::owns_lock();
183 }
184
185protected:
186 // needed for reverse_lock
188
189public:
194 public:
195 explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
196 CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
197 lock.unlock();
199 lock.swap(templock);
200 }
201
203 templock.swap(lock);
204 EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
205 lock.lock();
206 }
207
208 private:
211
214 std::string lockname;
215 const std::string file;
216 const int line;
217 };
218 friend class reverse_lock;
219};
220
221#define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
222
223template<typename MutexArg>
225
226#define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
227#define LOCK2(cs1, cs2) \
228 DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
229 DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
230#define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
231#define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
232
233#define ENTER_CRITICAL_SECTION(cs) \
234 { \
235 EnterCritical(#cs, __FILE__, __LINE__, &cs); \
236 (cs).lock(); \
237 }
238
239#define LEAVE_CRITICAL_SECTION(cs) \
240 { \
241 std::string lockname; \
242 CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \
243 (cs).unlock(); \
244 LeaveCritical(); \
245 }
246
270#define WITH_LOCK(cs, code) [&]() -> decltype(auto) { LOCK(cs); code; }()
271
273{
274private:
275 std::condition_variable condition;
276 std::mutex mutex;
277 int value;
278
279public:
280 explicit CSemaphore(int init) : value(init) {}
281
282 void wait()
283 {
284 std::unique_lock<std::mutex> lock(mutex);
285 condition.wait(lock, [&]() { return value >= 1; });
286 value--;
287 }
288
289 bool try_wait()
290 {
291 std::lock_guard<std::mutex> lock(mutex);
292 if (value < 1)
293 return false;
294 value--;
295 return true;
296 }
297
298 void post()
299 {
300 {
301 std::lock_guard<std::mutex> lock(mutex);
302 value++;
303 }
304 condition.notify_one();
305 }
306};
307
310{
311private:
314
315public:
316 void Acquire()
317 {
318 if (fHaveGrant)
319 return;
320 sem->wait();
321 fHaveGrant = true;
322 }
323
324 void Release()
325 {
326 if (!fHaveGrant)
327 return;
328 sem->post();
329 fHaveGrant = false;
330 }
331
333 {
334 if (!fHaveGrant && sem->try_wait())
335 fHaveGrant = true;
336 return fHaveGrant;
337 }
338
340 {
341 grant.Release();
342 grant.sem = sem;
343 grant.fHaveGrant = fHaveGrant;
344 fHaveGrant = false;
345 }
346
347 CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
348
349 explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false)
350 {
351 if (fTry)
352 TryAcquire();
353 else
354 Acquire();
355 }
356
358 {
359 Release();
360 }
361
362 operator bool() const
363 {
364 return fHaveGrant;
365 }
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:92
~AnnotatedMixin()
Definition: sync.h:94
bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
Definition: sync.h:108
void unlock() UNLOCK_FUNCTION()
Definition: sync.h:103
void lock() EXCLUSIVE_LOCK_FUNCTION()
Definition: sync.h:98
RAII-style semaphore lock.
Definition: sync.h:310
bool fHaveGrant
Definition: sync.h:313
CSemaphoreGrant(CSemaphore &sema, bool fTry=false)
Definition: sync.h:349
CSemaphoreGrant()
Definition: sync.h:347
void Release()
Definition: sync.h:324
bool TryAcquire()
Definition: sync.h:332
~CSemaphoreGrant()
Definition: sync.h:357
void MoveTo(CSemaphoreGrant &grant)
Definition: sync.h:339
void Acquire()
Definition: sync.h:316
CSemaphore * sem
Definition: sync.h:312
void wait()
Definition: sync.h:282
int value
Definition: sync.h:277
std::mutex mutex
Definition: sync.h:276
bool try_wait()
Definition: sync.h:289
CSemaphore(int init)
Definition: sync.h:280
std::condition_variable condition
Definition: sync.h:275
void post()
Definition: sync.h:298
An RAII-style reverse lock.
Definition: sync.h:193
reverse_lock(reverse_lock const &)
std::string lockname
Definition: sync.h:214
reverse_lock & operator=(reverse_lock const &)
const std::string file
Definition: sync.h:215
UniqueLock templock
Definition: sync.h:213
UniqueLock & lock
Definition: sync.h:212
reverse_lock(UniqueLock &_lock, const char *_guardname, const char *_file, int _line)
Definition: sync.h:195
Wrapper around std::unique_lock style lock for Mutex.
Definition: sync.h:134
bool TryEnter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:144
~UniqueLock() UNLOCK_FUNCTION()
Definition: sync.h:174
UniqueLock(Mutex &mutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(mutexIn)
Definition: sync.h:155
void Enter(const char *pszName, const char *pszFile, int nLine)
Definition: sync.h:136
UniqueLock()
Definition: sync.h:187
UniqueLock(Mutex *pmutexIn, const char *pszName, const char *pszFile, int nLine, bool fTry=false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
Definition: sync.h:163
static void pool cs
@ LOCK
Definition: logging.h:62
void AssertLockHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) EXCLUSIVE_LOCKS_REQUIRED(cs)
Definition: sync.h:77
void EnterCritical(const char *pszName, const char *pszFile, int nLine, MutexType *cs, bool fTry=false)
Definition: sync.h:73
void DeleteLock(void *cs)
Definition: sync.h:80
AnnotatedMixin< std::mutex > Mutex
Wrapped mutex: supports waiting but not recursive locking.
Definition: sync.h:129
void CheckLastCritical(void *cs, std::string &lockname, const char *guardname, const char *file, int line)
Definition: sync.h:75
void LeaveCritical()
Definition: sync.h:74
bool LockStackEmpty()
Definition: sync.h:81
void AssertLockNotHeldInternal(const char *pszName, const char *pszFile, int nLine, MutexType *cs) LOCKS_EXCLUDED(cs)
Definition: sync.h:79
#define EXCLUSIVE_LOCKS_REQUIRED(...)
Definition: threadsafety.h:49
#define EXCLUSIVE_TRYLOCK_FUNCTION(...)
Definition: threadsafety.h:44
#define LOCKS_EXCLUDED(...)
Definition: threadsafety.h:48
#define EXCLUSIVE_LOCK_FUNCTION(...)
Definition: threadsafety.h:42
#define SCOPED_LOCKABLE
Definition: threadsafety.h:37
#define LOCKABLE
Definition: threadsafety.h:36
#define UNLOCK_FUNCTION(...)
Definition: threadsafety.h:46
#define LOG_TIME_MICROS_WITH_CATEGORY(end_msg, log_category)
Definition: timer.h:90
#define strprintf
Format arguments and return the string or write to given std::ostream (see tinyformat::format doc for...
Definition: tinyformat.h:1164