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