]>
Commit | Line | Data |
---|---|---|
7f3ccb59 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2013 The Bitcoin Core developers |
78253fcb | 3 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
51ed9ec9 | 5 | |
7f3ccb59 PW |
6 | #ifndef BITCOIN_SYNC_H |
7 | #define BITCOIN_SYNC_H | |
8 | ||
51ed9ec9 BD |
9 | #include "threadsafety.h" |
10 | ||
8d3dd709 | 11 | #undef __cpuid |
51ed9ec9 BD |
12 | #include <boost/thread/condition_variable.hpp> |
13 | #include <boost/thread/locks.hpp> | |
660ff174 PW |
14 | #include <boost/thread/mutex.hpp> |
15 | #include <boost/thread/recursive_mutex.hpp> | |
7f3ccb59 | 16 | |
042da8bc EL |
17 | |
18 | //////////////////////////////////////////////// | |
19 | // // | |
45bfa137 | 20 | // THE SIMPLE DEFINITION, EXCLUDING DEBUG CODE // |
042da8bc EL |
21 | // // |
22 | //////////////////////////////////////////////// | |
23 | ||
24 | /* | |
042da8bc EL |
25 | CCriticalSection mutex; |
26 | boost::recursive_mutex mutex; | |
27 | ||
28 | LOCK(mutex); | |
29 | boost::unique_lock<boost::recursive_mutex> criticalblock(mutex); | |
30 | ||
31 | LOCK2(mutex1, mutex2); | |
32 | boost::unique_lock<boost::recursive_mutex> criticalblock1(mutex1); | |
33 | boost::unique_lock<boost::recursive_mutex> criticalblock2(mutex2); | |
34 | ||
35 | TRY_LOCK(mutex, name); | |
36 | boost::unique_lock<boost::recursive_mutex> name(mutex, boost::try_to_lock_t); | |
37 | ||
38 | ENTER_CRITICAL_SECTION(mutex); // no RAII | |
39 | mutex.lock(); | |
40 | ||
41 | LEAVE_CRITICAL_SECTION(mutex); // no RAII | |
42 | mutex.unlock(); | |
042da8bc EL |
43 | */ |
44 | ||
042da8bc EL |
45 | /////////////////////////////// |
46 | // // | |
47 | // THE ACTUAL IMPLEMENTATION // | |
48 | // // | |
49 | /////////////////////////////// | |
50 | ||
71ad6bd3 PK |
51 | /** |
52 | * Template mixin that adds -Wthread-safety locking | |
53 | * annotations to a subset of the mutex API. | |
54 | */ | |
05f97d12 AK |
55 | template <typename PARENT> |
56 | class LOCKABLE AnnotatedMixin : public PARENT | |
57 | { | |
58 | public: | |
59 | void lock() EXCLUSIVE_LOCK_FUNCTION() | |
60 | { | |
20e01b1a | 61 | PARENT::lock(); |
05f97d12 | 62 | } |
7f3ccb59 | 63 | |
05f97d12 AK |
64 | void unlock() UNLOCK_FUNCTION() |
65 | { | |
20e01b1a | 66 | PARENT::unlock(); |
05f97d12 | 67 | } |
7f3ccb59 | 68 | |
05f97d12 AK |
69 | bool try_lock() EXCLUSIVE_TRYLOCK_FUNCTION(true) |
70 | { | |
20e01b1a | 71 | return PARENT::try_lock(); |
05f97d12 AK |
72 | } |
73 | }; | |
7f3ccb59 | 74 | |
71ad6bd3 PK |
75 | /** |
76 | * Wrapped boost mutex: supports recursive locking, but no waiting | |
77 | * TODO: We should move away from using the recursive lock by default. | |
78 | */ | |
05f97d12 | 79 | typedef AnnotatedMixin<boost::recursive_mutex> CCriticalSection; |
7f3ccb59 PW |
80 | |
81 | /** Wrapped boost mutex: supports waiting but not recursive locking */ | |
05f97d12 | 82 | typedef AnnotatedMixin<boost::mutex> CWaitableCriticalSection; |
7f3ccb59 | 83 | |
ff6a7af1 LD |
84 | /** Just a typedef for boost::condition_variable, can be wrapped later if desired */ |
85 | typedef boost::condition_variable CConditionVariable; | |
86 | ||
7f3ccb59 PW |
87 | #ifdef DEBUG_LOCKORDER |
88 | void EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false); | |
89 | void LeaveCritical(); | |
c649637b | 90 | std::string LocksHeld(); |
20e01b1a | 91 | void AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs); |
7f3ccb59 | 92 | #else |
71ad6bd3 | 93 | void static inline EnterCritical(const char* pszName, const char* pszFile, int nLine, void* cs, bool fTry = false) {} |
7f3ccb59 | 94 | void static inline LeaveCritical() {} |
20e01b1a | 95 | void static inline AssertLockHeldInternal(const char* pszName, const char* pszFile, int nLine, void* cs) {} |
7f3ccb59 | 96 | #endif |
19a56762 | 97 | #define AssertLockHeld(cs) AssertLockHeldInternal(#cs, __FILE__, __LINE__, &cs) |
7f3ccb59 | 98 | |
4d009243 MC |
99 | #ifdef DEBUG_LOCKCONTENTION |
100 | void PrintLockContention(const char* pszName, const char* pszFile, int nLine); | |
101 | #endif | |
102 | ||
4d369ec3 | 103 | /** Wrapper around boost::unique_lock<Mutex> */ |
20e01b1a | 104 | template <typename Mutex> |
5093299a | 105 | class SCOPED_LOCKABLE CMutexLock |
7f3ccb59 PW |
106 | { |
107 | private: | |
660ff174 | 108 | boost::unique_lock<Mutex> lock; |
7f3ccb59 PW |
109 | |
110 | void Enter(const char* pszName, const char* pszFile, int nLine) | |
111 | { | |
bfc96207 | 112 | EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex())); |
7f3ccb59 | 113 | #ifdef DEBUG_LOCKCONTENTION |
20e01b1a | 114 | if (!lock.try_lock()) { |
bfc96207 | 115 | PrintLockContention(pszName, pszFile, nLine); |
7f3ccb59 | 116 | #endif |
20e01b1a | 117 | lock.lock(); |
7f3ccb59 | 118 | #ifdef DEBUG_LOCKCONTENTION |
7f3ccb59 | 119 | } |
bfc96207 | 120 | #endif |
7f3ccb59 PW |
121 | } |
122 | ||
123 | bool TryEnter(const char* pszName, const char* pszFile, int nLine) | |
124 | { | |
bfc96207 AK |
125 | EnterCritical(pszName, pszFile, nLine, (void*)(lock.mutex()), true); |
126 | lock.try_lock(); | |
660ff174 | 127 | if (!lock.owns_lock()) |
bfc96207 | 128 | LeaveCritical(); |
660ff174 | 129 | return lock.owns_lock(); |
7f3ccb59 PW |
130 | } |
131 | ||
bfc96207 | 132 | public: |
5093299a | 133 | CMutexLock(Mutex& mutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(mutexIn) : lock(mutexIn, boost::defer_lock) |
7f3ccb59 PW |
134 | { |
135 | if (fTry) | |
136 | TryEnter(pszName, pszFile, nLine); | |
137 | else | |
138 | Enter(pszName, pszFile, nLine); | |
139 | } | |
140 | ||
5093299a | 141 | CMutexLock(Mutex* pmutexIn, const char* pszName, const char* pszFile, int nLine, bool fTry = false) EXCLUSIVE_LOCK_FUNCTION(pmutexIn) |
4401b2d7 EL |
142 | { |
143 | if (!pmutexIn) return; | |
144 | ||
145 | lock = boost::unique_lock<Mutex>(*pmutexIn, boost::defer_lock); | |
146 | if (fTry) | |
147 | TryEnter(pszName, pszFile, nLine); | |
148 | else | |
149 | Enter(pszName, pszFile, nLine); | |
150 | } | |
151 | ||
5093299a | 152 | ~CMutexLock() UNLOCK_FUNCTION() |
7f3ccb59 | 153 | { |
660ff174 | 154 | if (lock.owns_lock()) |
7f3ccb59 PW |
155 | LeaveCritical(); |
156 | } | |
157 | ||
158 | operator bool() | |
159 | { | |
660ff174 | 160 | return lock.owns_lock(); |
7f3ccb59 | 161 | } |
7f3ccb59 PW |
162 | }; |
163 | ||
164 | typedef CMutexLock<CCriticalSection> CCriticalBlock; | |
165 | ||
166 | #define LOCK(cs) CCriticalBlock criticalblock(cs, #cs, __FILE__, __LINE__) | |
20e01b1a PW |
167 | #define LOCK2(cs1, cs2) CCriticalBlock criticalblock1(cs1, #cs1, __FILE__, __LINE__), criticalblock2(cs2, #cs2, __FILE__, __LINE__) |
168 | #define TRY_LOCK(cs, name) CCriticalBlock name(cs, #cs, __FILE__, __LINE__, true) | |
7f3ccb59 | 169 | |
20e01b1a PW |
170 | #define ENTER_CRITICAL_SECTION(cs) \ |
171 | { \ | |
7f3ccb59 | 172 | EnterCritical(#cs, __FILE__, __LINE__, (void*)(&cs)); \ |
20e01b1a | 173 | (cs).lock(); \ |
7f3ccb59 PW |
174 | } |
175 | ||
176 | #define LEAVE_CRITICAL_SECTION(cs) \ | |
20e01b1a PW |
177 | { \ |
178 | (cs).unlock(); \ | |
179 | LeaveCritical(); \ | |
7f3ccb59 PW |
180 | } |
181 | ||
7f3ccb59 PW |
182 | class CSemaphore |
183 | { | |
184 | private: | |
660ff174 PW |
185 | boost::condition_variable condition; |
186 | boost::mutex mutex; | |
187 | int value; | |
7f3ccb59 PW |
188 | |
189 | public: | |
660ff174 | 190 | CSemaphore(int init) : value(init) {} |
7f3ccb59 | 191 | |
20e01b1a PW |
192 | void wait() |
193 | { | |
660ff174 PW |
194 | boost::unique_lock<boost::mutex> lock(mutex); |
195 | while (value < 1) { | |
196 | condition.wait(lock); | |
197 | } | |
198 | value--; | |
7f3ccb59 PW |
199 | } |
200 | ||
20e01b1a PW |
201 | bool try_wait() |
202 | { | |
660ff174 PW |
203 | boost::unique_lock<boost::mutex> lock(mutex); |
204 | if (value < 1) | |
205 | return false; | |
206 | value--; | |
207 | return true; | |
7f3ccb59 PW |
208 | } |
209 | ||
20e01b1a PW |
210 | void post() |
211 | { | |
660ff174 PW |
212 | { |
213 | boost::unique_lock<boost::mutex> lock(mutex); | |
214 | value++; | |
215 | } | |
216 | condition.notify_one(); | |
7f3ccb59 PW |
217 | } |
218 | }; | |
7f3ccb59 PW |
219 | |
220 | /** RAII-style semaphore lock */ | |
221 | class CSemaphoreGrant | |
222 | { | |
223 | private: | |
20e01b1a | 224 | CSemaphore* sem; |
7f3ccb59 PW |
225 | bool fHaveGrant; |
226 | ||
227 | public: | |
20e01b1a PW |
228 | void Acquire() |
229 | { | |
7f3ccb59 PW |
230 | if (fHaveGrant) |
231 | return; | |
232 | sem->wait(); | |
233 | fHaveGrant = true; | |
234 | } | |
235 | ||
20e01b1a PW |
236 | void Release() |
237 | { | |
7f3ccb59 PW |
238 | if (!fHaveGrant) |
239 | return; | |
240 | sem->post(); | |
241 | fHaveGrant = false; | |
242 | } | |
243 | ||
20e01b1a PW |
244 | bool TryAcquire() |
245 | { | |
7f3ccb59 PW |
246 | if (!fHaveGrant && sem->try_wait()) |
247 | fHaveGrant = true; | |
248 | return fHaveGrant; | |
249 | } | |
250 | ||
20e01b1a PW |
251 | void MoveTo(CSemaphoreGrant& grant) |
252 | { | |
7f3ccb59 PW |
253 | grant.Release(); |
254 | grant.sem = sem; | |
255 | grant.fHaveGrant = fHaveGrant; | |
256 | sem = NULL; | |
257 | fHaveGrant = false; | |
258 | } | |
259 | ||
260 | CSemaphoreGrant() : sem(NULL), fHaveGrant(false) {} | |
261 | ||
20e01b1a PW |
262 | CSemaphoreGrant(CSemaphore& sema, bool fTry = false) : sem(&sema), fHaveGrant(false) |
263 | { | |
7f3ccb59 PW |
264 | if (fTry) |
265 | TryAcquire(); | |
266 | else | |
267 | Acquire(); | |
268 | } | |
269 | ||
20e01b1a PW |
270 | ~CSemaphoreGrant() |
271 | { | |
7f3ccb59 PW |
272 | Release(); |
273 | } | |
274 | ||
20e01b1a PW |
275 | operator bool() |
276 | { | |
7f3ccb59 PW |
277 | return fHaveGrant; |
278 | } | |
279 | }; | |
7f3ccb59 | 280 | |
093303a8 | 281 | #endif // BITCOIN_SYNC_H |