Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto
2 : // Copyright (c) 2009-2017 The Bitcoin developers
3 : // Copyright (c) 2017-2020 The PIVX Core developers
4 : // Distributed under the MIT/X11 software license, see the accompanying
5 : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 :
7 : #ifndef PIVX_SYNC_H
8 : #define PIVX_SYNC_H
9 :
10 : #include "threadsafety.h"
11 : #include "util/macros.h"
12 :
13 : #include <condition_variable>
14 : #include <mutex>
15 : #include <string>
16 : #include <thread>
17 :
18 :
19 : /////////////////////////////////////////////////
20 : // //
21 : // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE //
22 : // //
23 : /////////////////////////////////////////////////
24 :
25 : /*
26 : RecursiveMutex mutex;
27 : std::recursive_mutex mutex;
28 :
29 : LOCK(mutex);
30 : std::unique_lock<std::recursive_mutex> criticalblock(mutex);
31 :
32 : LOCK2(mutex1, mutex2);
33 : std::unique_lock<std::recursive_mutex> criticalblock1(mutex1);
34 : std::unique_lock<std::recursive_mutex> criticalblock2(mutex2);
35 :
36 : TRY_LOCK(mutex, name);
37 : std::unique_lock<std::recursive_mutex> name(mutex, std::try_to_lock_t);
38 :
39 : ENTER_CRITICAL_SECTION(mutex); // no RAII
40 : mutex.lock();
41 :
42 : LEAVE_CRITICAL_SECTION(mutex); // no RAII
43 : mutex.unlock();
44 : */
45 :
46 : ///////////////////////////////
47 : // //
48 : // THE ACTUAL IMPLEMENTATION //
49 : // //
50 : ///////////////////////////////
51 :
52 : #ifdef DEBUG_LOCKORDER
53 : void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false);
54 : void LeaveCritical();
55 : void CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line);
56 : std::string LocksHeld();
57 : void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
58 : void AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs);
59 : void DeleteLock(void* cs);
60 :
61 : /**
62 : * Call abort() if a potential lock order deadlock bug is detected, instead of
63 : * just logging information and throwing a logic_error. Defaults to true, and
64 : * set to false in DEBUG_LOCKORDER unit tests.
65 : */
66 : extern bool g_debug_lockorder_abort;
67 : #else
68 119635706 : void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {}
69 618725 : void static inline LeaveCritical() {}
70 618726 : void static inline CheckLastCritical(void* cs, std::string& lockname, const char* guardname, const char* file, int line) {}
71 64667537 : void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
72 204640 : void static inline AssertLockNotHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {}
73 30084 : void static inline DeleteLock(void* cs) {}
74 : #endif
75 : #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs)
76 : #define AssertLockNotHeld(cs) AssertLockNotHeldInternal(#cs, __FILE__, __LINE__, &cs)
77 :
78 : /**
79 : * Template mixin that adds -Wthread-safety locking annotations and lock order
80 : * checking to a subset of the mutex API.
81 : */
82 : template <typename PARENT>
83 32139 : class LOCKABLE AnnotatedMixin : public PARENT
84 : {
85 : public:
86 30084 : ~AnnotatedMixin() {
87 29042 : DeleteLock((void*)this);
88 19612 : }
89 :
90 7 : void lock() EXCLUSIVE_LOCK_FUNCTION()
91 : {
92 7 : PARENT::lock();
93 : }
94 :
95 7 : void unlock() UNLOCK_FUNCTION()
96 : {
97 14 : PARENT::unlock();
98 : }
99 :
100 : bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true)
101 : {
102 : return PARENT::try_lock();
103 : }
104 :
105 : using UniqueLock = std::unique_lock<PARENT>;
106 : };
107 :
108 : /**
109 : * Wrapped mutex: supports recursive locking, but no waiting
110 : * TODO: We should move away from using the recursive lock by default.
111 : */
112 : using RecursiveMutex = AnnotatedMixin<std::recursive_mutex>;
113 :
114 : /** Wrapped mutex: supports waiting but not recursive locking */
115 : typedef AnnotatedMixin<std::mutex> Mutex;
116 :
117 : #ifdef DEBUG_LOCKCONTENTION
118 : void PrintLockContention(const char* pszName, const char* pszFile, int nLine);
119 : #endif
120 :
121 : /** Wrapper around std::unique_lock style lock for Mutex. */
122 : template <typename Mutex, typename Base = typename Mutex::UniqueLock>
123 : class SCOPED_LOCKABLE UniqueLock : public Base
124 : {
125 : private:
126 117040807 : void Enter(const char* pszName, const char* pszFile, int nLine)
127 : {
128 117040807 : EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()));
129 : #ifdef DEBUG_LOCKCONTENTION
130 : if (!Base::try_lock()) {
131 : PrintLockContention(pszName, pszFile, nLine);
132 : #endif
133 117040807 : Base::lock();
134 : #ifdef DEBUG_LOCKCONTENTION
135 : }
136 : #endif
137 117040807 : }
138 :
139 1976126 : bool TryEnter(const char* pszName, const char* pszFile, int nLine)
140 : {
141 1976126 : EnterCritical(pszName, pszFile, nLine, (void*)(Base::mutex()), true);
142 1976126 : Base::try_lock();
143 119016997 : if (!Base::owns_lock())
144 : LeaveCritical();
145 119016997 : return Base::owns_lock();
146 : }
147 :
148 : public:
149 118913546 : UniqueLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : Base(mutexIn, std::defer_lock)
150 : {
151 118913546 : if (fTry)
152 118913546 : TryEnter(pszName, pszFile, nLine);
153 : else
154 118913546 : Enter(pszName, pszFile, nLine);
155 118913546 : }
156 :
157 103439 : UniqueLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn)
158 103439 : {
159 103439 : if (!pmutexIn) return;
160 :
161 103429 : *static_cast<Base*>(this) = Base(*pmutexIn, std::defer_lock);
162 103429 : if (fTry)
163 103439 : TryEnter(pszName, pszFile, nLine);
164 : else
165 103439 : Enter(pszName, pszFile, nLine);
166 : }
167 :
168 119635804 : ~UniqueLock() UNLOCK_FUNCTION()
169 : {
170 119016598 : if (Base::owns_lock())
171 : LeaveCritical();
172 218362797 : }
173 :
174 1976130 : operator bool()
175 : {
176 1976130 : return Base::owns_lock();
177 : }
178 :
179 : protected:
180 : // needed for reverse_lock
181 618726 : UniqueLock() { }
182 :
183 : public:
184 : /**
185 : * An RAII-style reverse lock. Unlocks on construction and locks on destruction.
186 : */
187 : class reverse_lock {
188 : public:
189 618728 : explicit reverse_lock(UniqueLock& _lock, const char* _guardname, const char* _file, int _line) : lock(_lock), file(_file), line(_line) {
190 618726 : CheckLastCritical((void*)lock.mutex(), lockname, _guardname, _file, _line);
191 618726 : lock.unlock();
192 : LeaveCritical();
193 618725 : lock.swap(templock);
194 618725 : }
195 :
196 618725 : ~reverse_lock() {
197 618725 : templock.swap(lock);
198 618725 : EnterCritical(lockname.c_str(), file.c_str(), line, (void*)lock.mutex());
199 618725 : lock.lock();
200 618729 : }
201 :
202 : private:
203 : reverse_lock(reverse_lock const&);
204 : reverse_lock& operator=(reverse_lock const&);
205 :
206 : UniqueLock& lock;
207 : UniqueLock templock;
208 : std::string lockname;
209 : const std::string file;
210 : const int line;
211 : };
212 : friend class reverse_lock;
213 : };
214 :
215 : #define REVERSE_LOCK(g) decltype(g)::reverse_lock PASTE2(revlock, __COUNTER__)(g, #g, __FILE__, __LINE__)
216 :
217 : template<typename MutexArg>
218 : using DebugLock = UniqueLock<typename std::remove_reference<typename std::remove_pointer<MutexArg>::type>::type>;
219 :
220 : #define LOCK(cs) DebugLock<decltype(cs)> PASTE2(criticalblock, __COUNTER__)(cs, #cs, __FILE__, __LINE__)
221 : #define LOCK2(cs1, cs2) \
222 : DebugLock<decltype(cs1)> criticalblock1(cs1, #cs1, __FILE__, __LINE__); \
223 : DebugLock<decltype(cs2)> criticalblock2(cs2, #cs2, __FILE__, __LINE__);
224 : #define TRY_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__, true)
225 : #define WAIT_LOCK(cs, name) DebugLock<decltype(cs)> name(cs, #cs, __FILE__, __LINE__)
226 :
227 : #define ENTER_CRITICAL_SECTION(cs) \
228 : { \
229 : EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \
230 : (cs).lock(); \
231 : }
232 :
233 : #define LEAVE_CRITICAL_SECTION(cs) \
234 : { \
235 : (cs).unlock(); \
236 : LeaveCritical(); \
237 : }
238 :
239 : //! Run code while locking a mutex.
240 : //!
241 : //! Examples:
242 : //!
243 : //! WITH_LOCK(cs, shared_val = shared_val + 1);
244 : //!
245 : //! int val = WITH_LOCK(cs, return shared_val);
246 : //!
247 : #define WITH_LOCK(cs, code) [&] { LOCK(cs); code; }()
248 :
249 710 : class CSemaphore
250 : {
251 : private:
252 : std::condition_variable condition;
253 : std::mutex mutex;
254 : int value;
255 :
256 : public:
257 710 : explicit CSemaphore(int init) : value(init) {}
258 :
259 47487 : void wait()
260 : {
261 47487 : std::unique_lock<std::mutex> lock(mutex);
262 47487 : condition.wait(lock, [&]() { return value >= 1; });
263 47487 : value--;
264 47487 : }
265 :
266 0 : bool try_wait()
267 : {
268 0 : std::lock_guard<std::mutex> lock(mutex);
269 0 : if (value < 1)
270 : return false;
271 0 : value--;
272 0 : return true;
273 : }
274 :
275 59202 : void post()
276 : {
277 59202 : {
278 59202 : std::lock_guard<std::mutex> lock(mutex);
279 59202 : value++;
280 : }
281 59202 : condition.notify_one();
282 59202 : }
283 : };
284 :
285 : /** RAII-style semaphore lock */
286 : class CSemaphoreGrant
287 : {
288 : private:
289 : CSemaphore* sem;
290 : bool fHaveGrant;
291 :
292 : public:
293 47487 : void Acquire()
294 : {
295 47487 : if (fHaveGrant)
296 : return;
297 47487 : sem->wait();
298 47487 : fHaveGrant = true;
299 : }
300 :
301 49627 : void Release()
302 : {
303 676 : if (!fHaveGrant)
304 : return;
305 0 : sem->post();
306 47487 : fHaveGrant = false;
307 : }
308 :
309 1 : bool TryAcquire()
310 : {
311 1 : if (!fHaveGrant && sem->try_wait())
312 0 : fHaveGrant = true;
313 1 : return fHaveGrant;
314 : }
315 :
316 0 : void MoveTo(CSemaphoreGrant& grant)
317 : {
318 0 : grant.Release();
319 0 : grant.sem = sem;
320 0 : grant.fHaveGrant = fHaveGrant;
321 0 : fHaveGrant = false;
322 0 : }
323 :
324 1464 : CSemaphoreGrant() : sem(nullptr), fHaveGrant(false) {}
325 :
326 47487 : explicit CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false)
327 : {
328 47487 : if (fTry)
329 0 : TryAcquire();
330 : else
331 47487 : Acquire();
332 47487 : }
333 :
334 48951 : ~CSemaphoreGrant()
335 1464 : {
336 96438 : Release();
337 0 : }
338 :
339 0 : operator bool() const
340 : {
341 0 : return fHaveGrant;
342 : }
343 : };
344 :
345 : #endif // PIVX_SYNC_H
|