sync.h raw
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2022 The Limenka 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 LIMENKA_SYNC_H
7 #define LIMENKA_SYNC_H
8
9 #ifdef DEBUG_LOCKCONTENTION
10 #include <logging.h>
11 #include <logging/timer.h>
12 #endif
13
14 #include <threadsafety.h> // IWYU pragma: export
15 #include <util/macros.h>
16
17 #include <condition_variable>
18 #include <mutex>
19 #include <string>
20 #include <thread>
21
22 ////////////////////////////////////////////////
23 // //
24 // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
25 // //
26 ////////////////////////////////////////////////
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
49 ///////////////////////////////
50 // //
51 // THE ACTUAL IMPLEMENTATION //
52 // //
53 ///////////////////////////////
54
55 #ifdef DEBUG_LOCKORDER
56 template <typename MutexType>
57 void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false);
58 void LeaveCritical();
59 void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
60 template <typename MutexType>
61 void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs);
62 template <typename MutexType>
63 void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs);
64 void DeleteLock(void* cs);
65 bool LockStackEmpty();
66
67 /**
68 * Call abort() if a potential lock order deadlock bug is detected, instead of
69 * just logging information and throwing a logic_error. Defaults to true, and
70 * set to false in DEBUG_LOCKORDER unit tests.
71 */
72 extern bool g_debug_lockorder_abort;
73 #else
74 template <typename MutexType>
75 inline void EnterCritical(const char* pszName, const char* pszFile, int nLine, MutexType* cs, bool fTry = false) {}
76 inline void LeaveCritical() {}
77 inline void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
78 template <typename MutexType>
79 inline void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) EXCLUSIVE_LOCKS_REQUIRED(cs) {}
80 template <typename MutexType>
81 void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, MutexType* cs) LOCKS_EXCLUDED(cs) {}
82 inline void DeleteLock(void* cs) {}
83 inline bool LockStackEmpty() { return true; }
84 #endif
85
86 /**
87 * Template mixin that adds -Wthread-safety locking annotations and lock order
88 * checking to a subset of the mutex API.
89 */
90 template <typename PARENT>
91 class LOCKABLE AnnotatedMixin : public PARENT
92 {
93 public:
94 ~AnnotatedMixin() {
95 DeleteLock((void*)this);
96 }
97
98 void lock() EXCLUSIVE_LOCK_FUNCTION()
99 {
100 PARENT::lock();
101 }
102
103 void unlock() UNLOCK_FUNCTION()
104 {
105 PARENT::unlock();
106 }
107
108 bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
109 {
110 return PARENT::try_lock();
111 }
112
113 using unique_lock = std::unique_lock<PARENT>;
114 #ifdef __clang__
115 //! For negative capabilities in the Clang Thread Safety Analysis.
116 //! A negative requirement uses the EXCLUSIVE_LOCKS_REQUIRED attribute, in conjunction
117 //! with the ! operator, to indicate that a mutex should not be held.
118 const AnnotatedMixin& operator!() const { return *this; }
119 #endif // __clang__
120 };
121
122 /**
123 * Wrapped mutex: supports recursive locking, but no waiting
124 * TODO: We should move away from using the recursive lock by default.
125 */
126 using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
127
128 /** Wrapped mutex: supports waiting but not recursive locking */
129 using Mutex = AnnotatedMixin<std::mutex>;
130
131 /** Different type to mark Mutex at global scope
132 *
133 * Thread safety analysis can't handle negative assertions about mutexes
134 * with global scope well, so mark them with a separate type, and
135 * eventually move all the mutexes into classes so they are not globally
136 * visible.
137 *
138 * See: https://github.com/limenka/limenka/pull/20272#issuecomment-720755781
139 */
140 class GlobalMutex : public Mutex { };
141
142 #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
143
144 inline void AssertLockNotHeldInline(const char* name, const char* file, int line, Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) { AssertLockNotHeldInternal(name, file, line, cs); }
145 inline void AssertLockNotHeldInline(const char* name, const char* file, int line, RecursiveMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
146 inline void AssertLockNotHeldInline(const char* name, const char* file, int line, GlobalMutex* cs) LOCKS_EXCLUDED(cs) { AssertLockNotHeldInternal(name, file, line, cs); }
147 #define AssertLockNotHeld(cs) AssertLockNotHeldInline(#cs, __FILE__, __LINE__, &cs)
148
149 /** Wrapper around std::unique_lock style lock for MutexType. */
150 template <typename MutexType>
151 class SCOPED_LOCKABLE UniqueLock : public MutexType::unique_lock
152 {
153 private:
154 using Base = typename MutexType::unique_lock;
155
156 void Enter(const char* pszName, const char* pszFile, int nLine)
157 {
158 EnterCritical(pszName, pszFile, nLine, Base::mutex());
159 #ifdef DEBUG_LOCKCONTENTION
160 if (Base::try_lock()) return;
161 LOG_TIME_MICROS_WITH_CATEGORY(strprintf("lock contention %s, %s:%d", pszName, pszFile, nLine), BCLog::LOCK);
162 #endif
163 Base::lock();
164 }
165
166 bool TryEnter(const char* pszName, const char* pszFile, int nLine)
167 {
168 EnterCritical(pszName, pszFile, nLine, Base::mutex(), true);
169 if (Base::try_lock()) {
170 return true;
171 }
172 LeaveCritical();
173 return false;
174 }
175
176 public:
177 UniqueLock(MutexType& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
178 {
179 if (fTry)
180 TryEnter(pszName, pszFile, nLine);
181 else
182 Enter(pszName, pszFile, nLine);
183 }
184
185 UniqueLock(MutexType* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
186 {
187 if (!pmutexIn) return;
188
189 *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
190 if (fTry)
191 TryEnter(pszName, pszFile, nLine);
192 else
193 Enter(pszName, pszFile, nLine);
194 }
195
196 ~UniqueLock() UNLOCK_FUNCTION()
197 {
198 if (Base::owns_lock())
199 LeaveCritical();
200 }
201
202 operator bool()
203 {
204 return Base::owns_lock();
205 }
206
207 protected:
208 // needed for reverse_lock
209 UniqueLock() = default;
210
211 public:
212 /**
213 * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
214 */
215 class reverse_lock {
216 public:
217 explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
218 CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
219 lock.unlock();
220 LeaveCritical();
221 lock.swap(templock);
222 }
223
224 ~reverse_lock() {
225 templock.swap(lock);
226 EnterCritical(lockname.c_str(), file.c_str(), line, lock.mutex());
227 lock.lock();
228 }
229
230 private:
231 reverse_lock(reverse_lock const&);
232 reverse_lock& operator=(reverse_lock const&);
233
234 UniqueLock& lock;
235 UniqueLock templock;
236 std::string lockname;
237 const std::string file;
238 const int line;
239 };
240 friend class reverse_lock;
241 };
242
243 #define REVERSE_LOCK(g) typename std::decay<decltype(g)>::type::reverse_lock UNIQUE_NAME(revlock)(g, #g, __FILE__, __LINE__)
244
245 // When locking a Mutex, require negative capability to ensure the lock
246 // is not already held
247 inline Mutex& MaybeCheckNotHeld(Mutex& cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
248 inline Mutex* MaybeCheckNotHeld(Mutex* cs) EXCLUSIVE_LOCKS_REQUIRED(!cs) LOCK_RETURNED(cs) { return cs; }
249
250 // When locking a GlobalMutex or RecursiveMutex, just check it is not
251 // locked in the surrounding scope.
252 template <typename MutexType>
253 inline MutexType& MaybeCheckNotHeld(MutexType& m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
254 template <typename MutexType>
255 inline MutexType* MaybeCheckNotHeld(MutexType* m) LOCKS_EXCLUDED(m) LOCK_RETURNED(m) { return m; }
256
257 #define LOCK(cs) UniqueLock UNIQUE_NAME(criticalblock)(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
258 #define LOCK2(cs1, cs2) \
259 UniqueLock criticalblock1(MaybeCheckNotHeld(cs1), #cs1, __FILE__, __LINE__); \
260 UniqueLock criticalblock2(MaybeCheckNotHeld(cs2), #cs2, __FILE__, __LINE__)
261 #define TRY_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__, true)
262 #define WAIT_LOCK(cs, name) UniqueLock name(MaybeCheckNotHeld(cs), #cs, __FILE__, __LINE__)
263
264 #define ENTER_CRITICAL_SECTION(cs) \
265 { \
266 EnterCritical(#cs, __FILE__, __LINE__, &cs); \
267 (cs).lock(); \
268 }
269
270 #define LEAVE_CRITICAL_SECTION(cs) \
271 { \
272 std::string lockname; \
273 CheckLastCritical((void*)(&cs), lockname, #cs, __FILE__, __LINE__); \
274 (cs).unlock(); \
275 LeaveCritical(); \
276 }
277
278 //! Run code while locking a mutex.
279 //!
280 //! Examples:
281 //!
282 //! WITH_LOCK(cs, shared_val = shared_val + 1);
283 //!
284 //! int val = WITH_LOCK(cs, return shared_val);
285 //!
286 //! Note:
287 //!
288 //! Since the return type deduction follows that of decltype(auto), while the
289 //! deduced type of:
290 //!
291 //! WITH_LOCK(cs, return {int i = 1; return i;});
292 //!
293 //! is int, the deduced type of:
294 //!
295 //! WITH_LOCK(cs, return {int j = 1; return (j);});
296 //!
297 //! is &int, a reference to a local variable
298 //!
299 //! The above is detectable at compile-time with the -Wreturn-local-addr flag in
300 //! gcc and the -Wreturn-stack-address flag in clang, both enabled by default.
301 #define WITH_LOCK(cs, code) (MaybeCheckNotHeld(cs), [&]() -> decltype(auto) { LOCK(cs); code; }())
302
303 /** An implementation of a semaphore.
304 *
305 * See https://en.wikipedia.org/wiki/Semaphore_(programming)
306 */
307 class CSemaphore
308 {
309 private:
310 std::condition_variable condition;
311 std::mutex mutex;
312 int value;
313
314 public:
315 explicit CSemaphore(int init) noexcept : value(init) {}
316
317 // Disallow default construct, copy, move.
318 CSemaphore() = delete;
319 CSemaphore(const CSemaphore&) = delete;
320 CSemaphore(CSemaphore&&) = delete;
321 CSemaphore& operator=(const CSemaphore&) = delete;
322 CSemaphore& operator=(CSemaphore&&) = delete;
323
324 void wait() noexcept
325 {
326 std::unique_lock<std::mutex> lock(mutex);
327 condition.wait(lock, [&]() { return value >= 1; });
328 value--;
329 }
330
331 bool try_wait() noexcept
332 {
333 std::lock_guard<std::mutex> lock(mutex);
334 if (value < 1) {
335 return false;
336 }
337 value--;
338 return true;
339 }
340
341 void post() noexcept
342 {
343 {
344 std::lock_guard<std::mutex> lock(mutex);
345 value++;
346 }
347 condition.notify_one();
348 }
349 };
350
351 /** RAII-style semaphore lock */
352 class CSemaphoreGrant
353 {
354 private:
355 CSemaphore* sem;
356 bool fHaveGrant;
357
358 public:
359 void Acquire() noexcept
360 {
361 if (fHaveGrant) {
362 return;
363 }
364 sem->wait();
365 fHaveGrant = true;
366 }
367
368 void Release() noexcept
369 {
370 if (!fHaveGrant) {
371 return;
372 }
373 sem->post();
374 fHaveGrant = false;
375 }
376
377 bool TryAcquire() noexcept
378 {
379 if (!fHaveGrant && sem->try_wait()) {
380 fHaveGrant = true;
381 }
382 return fHaveGrant;
383 }
384
385 // Disallow copy.
386 CSemaphoreGrant(const CSemaphoreGrant&) = delete;
387 CSemaphoreGrant& operator=(const CSemaphoreGrant&) = delete;
388
389 // Allow move.
390 CSemaphoreGrant(CSemaphoreGrant&& other) noexcept
391 {
392 sem = other.sem;
393 fHaveGrant = other.fHaveGrant;
394 other.fHaveGrant = false;
395 other.sem = nullptr;
396 }
397
398 CSemaphoreGrant& operator=(CSemaphoreGrant&& other) noexcept
399 {
400 Release();
401 sem = other.sem;
402 fHaveGrant = other.fHaveGrant;
403 other.fHaveGrant = false;
404 other.sem = nullptr;
405 return *this;
406 }
407
408 CSemaphoreGrant() noexcept : sem(nullptr), fHaveGrant(false) {}
409
410 explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) noexcept : sem(&sema), fHaveGrant(false)
411 {
412 if (fTry) {
413 TryAcquire();
414 } else {
415 Acquire();
416 }
417 }
418
419 ~CSemaphoreGrant()
420 {
421 Release();
422 }
423
424 explicit operator bool() const noexcept
425 {
426 return fHaveGrant;
427 }
428 };
429
430 #endif // LIMENKA_SYNC_H
431