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