1 | /* |
2 | * PROGRAM: Client/Server Common Code |
3 | * MODULE: locks.h |
4 | * DESCRIPTION: Single-state locks |
5 | * |
6 | * The contents of this file are subject to the Initial |
7 | * Developer's Public License Version 1.0 (the "License"); |
8 | * you may not use this file except in compliance with the |
9 | * License. You may obtain a copy of the License at |
10 | * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. |
11 | * |
12 | * Software distributed under the License is distributed AS IS, |
13 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. |
14 | * See the License for the specific language governing rights |
15 | * and limitations under the License. |
16 | * |
17 | * The Original Code was created by Nickolay Samofatov |
18 | * for the Firebird Open Source RDBMS project. |
19 | * |
20 | * Copyright (c) 2004 Nickolay Samofatov <nickolay@broadviewsoftware.com> |
21 | * and all contributors signed below. |
22 | * |
23 | * All Rights Reserved. |
24 | * Contributor(s): ______________________________________. |
25 | * |
26 | * |
27 | */ |
28 | |
29 | #ifndef CLASSES_LOCKS_H |
30 | #define CLASSES_LOCKS_H |
31 | |
32 | #include "firebird.h" |
33 | #include "../common/gdsassert.h" |
34 | #include "../common/classes/Reasons.h" |
35 | |
36 | #ifdef WIN_NT |
37 | // It is relatively easy to avoid using this header. Maybe do the same stuff like |
38 | // in thd.h ? This is Windows platform maintainers choice |
39 | #include <windows.h> |
40 | #else |
41 | #include "fb_pthread.h" |
42 | #include <errno.h> |
43 | #endif |
44 | |
45 | namespace Firebird { |
46 | |
47 | class MemoryPool; // Needed for ctors that must always ignore it |
48 | class Exception; // Needed for catch |
49 | |
50 | #ifdef WIN_NT |
51 | |
52 | // Generic process-local mutex and spinlock. The latter |
53 | // is used to manage memory heaps in a threaded environment. |
54 | |
55 | // Windows version of the class |
56 | |
57 | class TryEnterCS |
58 | { |
59 | public: |
60 | TryEnterCS(); |
61 | |
62 | static bool tryEnter(LPCRITICAL_SECTION lpCS) |
63 | { |
64 | return ((*m_funct) (lpCS) == TRUE); |
65 | } |
66 | |
67 | private: |
68 | typedef WINBASEAPI BOOL WINAPI tTryEnterCriticalSection |
69 | (LPCRITICAL_SECTION lpCriticalSection); |
70 | |
71 | static tTryEnterCriticalSection* m_funct; |
72 | }; |
73 | |
74 | class Mutex : public Reasons |
75 | { |
76 | protected: |
77 | CRITICAL_SECTION spinlock; |
78 | #ifdef DEV_BUILD |
79 | int lockCount; |
80 | #endif |
81 | |
82 | public: |
83 | Mutex() |
84 | #ifdef DEV_BUILD |
85 | : lockCount(0) |
86 | #endif |
87 | { |
88 | InitializeCriticalSection(&spinlock); |
89 | } |
90 | explicit Mutex(MemoryPool&) |
91 | #ifdef DEV_BUILD |
92 | : lockCount(0) |
93 | #endif |
94 | { |
95 | InitializeCriticalSection(&spinlock); |
96 | } |
97 | |
98 | ~Mutex() |
99 | { |
100 | #if defined DEV_BUILD && !defined WIN9X_SUPPORT |
101 | if (spinlock.OwningThread != 0) |
102 | DebugBreak(); |
103 | fb_assert(lockCount == 0); |
104 | #endif |
105 | DeleteCriticalSection(&spinlock); |
106 | } |
107 | |
108 | void enter(const char* aReason) |
109 | { |
110 | EnterCriticalSection(&spinlock); |
111 | reason(aReason); |
112 | #ifdef DEV_BUILD |
113 | lockCount++; |
114 | #endif |
115 | } |
116 | |
117 | bool tryEnter(const char* aReason) |
118 | { |
119 | const bool ret = TryEnterCS::tryEnter(&spinlock); |
120 | if (ret) |
121 | { |
122 | reason(aReason); |
123 | #ifdef DEV_BUILD |
124 | lockCount++; |
125 | #endif |
126 | } |
127 | return ret; |
128 | } |
129 | |
130 | void leave() |
131 | { |
132 | #if defined DEV_BUILD && !defined WIN9X_SUPPORT |
133 | // NS: This check is based on internal structure on CRITICAL_SECTION |
134 | // On 9X it works differently, and future OS versions may break this check as well |
135 | if ((U_IPTR) spinlock.OwningThread != GetCurrentThreadId()) |
136 | DebugBreak(); |
137 | |
138 | lockCount--; |
139 | #endif |
140 | LeaveCriticalSection(&spinlock); |
141 | } |
142 | |
143 | #ifdef DEV_BUILD |
144 | bool locked() |
145 | { |
146 | // first of all try to enter the mutex |
147 | // this will help to make sure it's not locked by other thread |
148 | if (!tryEnter(FB_FUNCTION)) |
149 | { |
150 | return false; |
151 | } |
152 | // make sure mutex was already locked |
153 | bool rc = lockCount > 1; |
154 | // leave to release lock, done by us in tryEnter |
155 | leave(); |
156 | |
157 | return rc; |
158 | } |
159 | #endif |
160 | |
161 | public: |
162 | static void initMutexes() { } |
163 | |
164 | private: |
165 | // Forbid copying |
166 | Mutex(const Mutex&); |
167 | Mutex& operator=(const Mutex&); |
168 | }; |
169 | |
170 | class Spinlock : public Mutex |
171 | { |
172 | private: |
173 | void init(); |
174 | |
175 | public: |
176 | Spinlock() |
177 | { |
178 | init(); |
179 | } |
180 | |
181 | explicit Spinlock(MemoryPool&) |
182 | { |
183 | init(); |
184 | } |
185 | }; |
186 | |
187 | #else //WIN_NT |
188 | |
189 | // Pthreads version of the class |
190 | class Mutex : public Reasons |
191 | { |
192 | friend class Condition; |
193 | private: |
194 | pthread_mutex_t mlock; |
195 | static pthread_mutexattr_t attr; |
196 | #ifdef DEV_BUILD |
197 | int lockCount; |
198 | #endif |
199 | |
200 | private: |
201 | void init() |
202 | { |
203 | #ifdef DEV_BUILD |
204 | lockCount = 0; |
205 | #endif |
206 | int rc = pthread_mutex_init(&mlock, &attr); |
207 | if (rc) |
208 | system_call_failed::raise("pthread_mutex_init" , rc); |
209 | } |
210 | |
211 | public: |
212 | Mutex() { init(); } |
213 | explicit Mutex(MemoryPool&) { init(); } |
214 | |
215 | ~Mutex() |
216 | { |
217 | fb_assert(lockCount == 0); |
218 | int rc = pthread_mutex_destroy(&mlock); |
219 | if (rc) |
220 | system_call_failed::raise("pthread_mutex_destroy" , rc); |
221 | } |
222 | |
223 | void enter(const char* aReason) |
224 | { |
225 | int rc = pthread_mutex_lock(&mlock); |
226 | if (rc) |
227 | system_call_failed::raise("pthread_mutex_lock" , rc); |
228 | reason(aReason); |
229 | #ifdef DEV_BUILD |
230 | ++lockCount; |
231 | #endif |
232 | } |
233 | |
234 | bool tryEnter(const char* aReason) |
235 | { |
236 | int rc = pthread_mutex_trylock(&mlock); |
237 | if (rc == EBUSY) |
238 | return false; |
239 | if (rc) |
240 | system_call_failed::raise("pthread_mutex_trylock" , rc); |
241 | #ifdef DEV_BUILD |
242 | reason(aReason); |
243 | ++lockCount; |
244 | #endif |
245 | return true; |
246 | } |
247 | |
248 | void leave() |
249 | { |
250 | #ifdef DEV_BUILD |
251 | fb_assert(lockCount > 0); |
252 | --lockCount; |
253 | #endif |
254 | int rc = pthread_mutex_unlock(&mlock); |
255 | if (rc) |
256 | { |
257 | #ifdef DEV_BUILD |
258 | ++lockCount; |
259 | #endif |
260 | system_call_failed::raise("pthread_mutex_unlock" , rc); |
261 | } |
262 | } |
263 | |
264 | #ifdef DEV_BUILD |
265 | bool locked() |
266 | { |
267 | // first of all try to enter the mutex |
268 | // this will help to make sure it's not locked by other thread |
269 | if (!tryEnter(FB_FUNCTION)) |
270 | { |
271 | return false; |
272 | } |
273 | // make sure mutex was already locked |
274 | bool rc = lockCount > 1; |
275 | // leave to release lock, done by us in tryEnter |
276 | leave(); |
277 | |
278 | return rc; |
279 | } |
280 | #endif |
281 | |
282 | public: |
283 | static void initMutexes(); |
284 | |
285 | private: |
286 | // Forbid copying |
287 | Mutex(const Mutex&); |
288 | Mutex& operator=(const Mutex&); |
289 | }; |
290 | |
291 | #ifdef NOT_USED_OR_REPLACED // we do not use spinlocks currently |
292 | class Spinlock |
293 | { |
294 | private: |
295 | pthread_spinlock_t spinlock; |
296 | public: |
297 | Spinlock() |
298 | { |
299 | if (pthread_spin_init(&spinlock, false)) |
300 | system_call_failed::raise("pthread_spin_init" ); |
301 | } |
302 | |
303 | explicit Spinlock(MemoryPool&) |
304 | { |
305 | if (pthread_spin_init(&spinlock, false)) |
306 | system_call_failed::raise("pthread_spin_init" ); |
307 | } |
308 | |
309 | ~Spinlock() |
310 | { |
311 | if (pthread_spin_destroy(&spinlock)) |
312 | system_call_failed::raise("pthread_spin_destroy" ); |
313 | } |
314 | |
315 | void enter() |
316 | { |
317 | if (pthread_spin_lock(&spinlock)) |
318 | system_call_failed::raise("pthread_spin_lock" ); |
319 | } |
320 | |
321 | void leave() |
322 | { |
323 | if (pthread_spin_unlock(&spinlock)) |
324 | system_call_failed::raise("pthread_spin_unlock" ); |
325 | } |
326 | |
327 | private: |
328 | // Forbid copying |
329 | Spinlock(const Spinlock&); |
330 | Spinlock& operator=(const Spinlock&); |
331 | }; |
332 | #else |
333 | typedef Mutex Spinlock; |
334 | #endif |
335 | |
336 | #endif //WIN_NT |
337 | |
338 | |
339 | // RAII holder |
340 | class MutexLockGuard |
341 | { |
342 | public: |
343 | MutexLockGuard(Mutex& aLock, const char* aReason) |
344 | : lock(&aLock) |
345 | { |
346 | lock->enter(aReason); |
347 | } |
348 | |
349 | ~MutexLockGuard() |
350 | { |
351 | try |
352 | { |
353 | lock->leave(); |
354 | } |
355 | catch (const Exception&) |
356 | { |
357 | DtorException::devHalt(); |
358 | } |
359 | } |
360 | |
361 | private: |
362 | // Forbid copying |
363 | MutexLockGuard(const MutexLockGuard&); |
364 | MutexLockGuard& operator=(const MutexLockGuard&); |
365 | |
366 | Mutex* lock; |
367 | }; |
368 | |
369 | class MutexUnlockGuard |
370 | { |
371 | public: |
372 | explicit MutexUnlockGuard(Mutex& aLock, const char* aReason) |
373 | : lock(&aLock) |
374 | #ifdef DEV_BUILD |
375 | , saveReason(aReason) |
376 | #endif |
377 | { |
378 | lock->leave(); |
379 | } |
380 | |
381 | ~MutexUnlockGuard() |
382 | { |
383 | try |
384 | { |
385 | #ifdef DEV_BUILD |
386 | lock->enter(saveReason); |
387 | #else |
388 | lock->enter(NULL); |
389 | #endif |
390 | } |
391 | catch (const Exception&) |
392 | { |
393 | DtorException::devHalt(); |
394 | } |
395 | } |
396 | |
397 | private: |
398 | // Forbid copying |
399 | MutexUnlockGuard(const MutexUnlockGuard&); |
400 | MutexUnlockGuard& operator=(const MutexUnlockGuard&); |
401 | |
402 | Mutex* lock; |
403 | #ifdef DEV_BUILD |
404 | const char* saveReason; |
405 | #endif |
406 | }; |
407 | |
408 | |
409 | class MutexCheckoutGuard |
410 | { |
411 | public: |
412 | MutexCheckoutGuard(Mutex& mtxCout, Mutex& mtxLock, const char* aReason) |
413 | : unlock(mtxCout, aReason), |
414 | lock(mtxLock, aReason) |
415 | { |
416 | } |
417 | |
418 | private: |
419 | // Forbid copying |
420 | MutexCheckoutGuard(const MutexCheckoutGuard&); |
421 | MutexCheckoutGuard& operator=(const MutexCheckoutGuard&); |
422 | |
423 | MutexUnlockGuard unlock; |
424 | MutexLockGuard lock; |
425 | }; |
426 | |
427 | } //namespace Firebird |
428 | |
429 | #endif // CLASSES_LOCKS_H |
430 | |