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
45namespace Firebird {
46
47class MemoryPool; // Needed for ctors that must always ignore it
48class 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
57class TryEnterCS
58{
59public:
60 TryEnterCS();
61
62 static bool tryEnter(LPCRITICAL_SECTION lpCS)
63 {
64 return ((*m_funct) (lpCS) == TRUE);
65 }
66
67private:
68 typedef WINBASEAPI BOOL WINAPI tTryEnterCriticalSection
69 (LPCRITICAL_SECTION lpCriticalSection);
70
71 static tTryEnterCriticalSection* m_funct;
72};
73
74class Mutex : public Reasons
75{
76protected:
77 CRITICAL_SECTION spinlock;
78#ifdef DEV_BUILD
79 int lockCount;
80#endif
81
82public:
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
161public:
162 static void initMutexes() { }
163
164private:
165 // Forbid copying
166 Mutex(const Mutex&);
167 Mutex& operator=(const Mutex&);
168};
169
170class Spinlock : public Mutex
171{
172private:
173 void init();
174
175public:
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
190class Mutex : public Reasons
191{
192friend class Condition;
193private:
194 pthread_mutex_t mlock;
195 static pthread_mutexattr_t attr;
196#ifdef DEV_BUILD
197 int lockCount;
198#endif
199
200private:
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
211public:
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
282public:
283 static void initMutexes();
284
285private:
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
292class Spinlock
293{
294private:
295 pthread_spinlock_t spinlock;
296public:
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
327private:
328 // Forbid copying
329 Spinlock(const Spinlock&);
330 Spinlock& operator=(const Spinlock&);
331};
332#else
333typedef Mutex Spinlock;
334#endif
335
336#endif //WIN_NT
337
338
339// RAII holder
340class MutexLockGuard
341{
342public:
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
361private:
362 // Forbid copying
363 MutexLockGuard(const MutexLockGuard&);
364 MutexLockGuard& operator=(const MutexLockGuard&);
365
366 Mutex* lock;
367};
368
369class MutexUnlockGuard
370{
371public:
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
397private:
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
409class MutexCheckoutGuard
410{
411public:
412 MutexCheckoutGuard(Mutex& mtxCout, Mutex& mtxLock, const char* aReason)
413 : unlock(mtxCout, aReason),
414 lock(mtxLock, aReason)
415 {
416 }
417
418private:
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