e3dbb31d06e214c5fd6e4d5d1afe4d4e70dde81b
[public/netxms.git] / src / libnetxms / rwlock.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** NetXMS Foundation Library
4 ** Copyright (C) 2003, 2004, 2005, 2006 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: rwlock.cpp
21 **
22 **/
23
24 #include "libnetxms.h"
25 #include <assert.h>
26
27 #if !HAVE_PTHREAD_RWLOCK && !defined(_USE_GNU_PTH)
28
29 //
30 // Create read/write lock
31 //
32
33 RWLOCK LIBNETXMS_EXPORTABLE RWLockCreate(void)
34 {
35 RWLOCK hLock;
36
37 hLock = (RWLOCK)malloc(sizeof(struct __rwlock_data));
38 if (hLock != NULL)
39 {
40 #ifdef _WIN32
41 hLock->m_mutex = CreateMutex(NULL, FALSE, NULL);
42 hLock->m_condRead = CreateEvent(NULL, TRUE, FALSE, NULL);
43 hLock->m_condWrite = CreateEvent(NULL, FALSE, FALSE, NULL);
44 #else
45 pthread_mutex_init(&hLock->m_mutex, NULL);
46 pthread_cond_init(&hLock->m_condRead, NULL);
47 pthread_cond_init(&hLock->m_condWrite, NULL);
48 #endif
49 hLock->m_dwWaitReaders = 0;
50 hLock->m_dwWaitWriters = 0;
51 hLock->m_iRefCount = 0;
52 }
53
54 return hLock;
55 }
56
57
58 //
59 // Destroy read/write lock
60 //
61
62 void LIBNETXMS_EXPORTABLE RWLockDestroy(RWLOCK hLock)
63 {
64 if (hLock != NULL)
65 {
66 if (hLock->m_iRefCount == 0)
67 {
68 #ifdef _WIN32
69 CloseHandle(hLock->m_mutex);
70 CloseHandle(hLock->m_condRead);
71 CloseHandle(hLock->m_condWrite);
72 #else
73 pthread_mutex_destroy(&hLock->m_mutex);
74 pthread_cond_destroy(&hLock->m_condRead);
75 pthread_cond_destroy(&hLock->m_condWrite);
76 #endif
77 free(hLock);
78 }
79 }
80 }
81
82
83 //
84 // Lock read/write lock for reading
85 //
86
87 BOOL LIBNETXMS_EXPORTABLE RWLockReadLock(RWLOCK hLock, DWORD dwTimeOut)
88 {
89 BOOL bResult = FALSE;
90 int retcode;
91
92 // Check if handle is valid
93 if (hLock == NULL)
94 return FALSE;
95
96 #ifdef _WIN32
97 DWORD dwStart, dwElapsed;
98 BOOL bTimeOut = FALSE;
99
100 // Acquire access to handle
101 WaitForSingleObject(hLock->m_mutex, INFINITE);
102
103 do
104 {
105 if ((hLock->m_iRefCount == -1) || (hLock->m_dwWaitWriters > 0))
106 {
107 // Object is locked for writing or somebody wish to lock it for writing
108 hLock->m_dwWaitReaders++;
109 ReleaseMutex(hLock->m_mutex);
110 dwStart = GetTickCount();
111 retcode = WaitForSingleObject(hLock->m_condRead, dwTimeOut);
112 dwElapsed = GetTickCount() - dwStart;
113 WaitForSingleObject(hLock->m_mutex, INFINITE); // Re-acquire mutex
114 hLock->m_dwWaitReaders--;
115 if (retcode == WAIT_TIMEOUT)
116 {
117 bTimeOut = TRUE;
118 }
119 else
120 {
121 if (dwTimeOut != INFINITE)
122 {
123 dwTimeOut -= min(dwElapsed, dwTimeOut);
124 }
125 }
126 }
127 else
128 {
129 hLock->m_iRefCount++;
130 bResult = TRUE;
131 }
132 } while((!bResult) && (!bTimeOut));
133
134 ReleaseMutex(hLock->m_mutex);
135 #else
136 // Acquire access to handle
137 if (pthread_mutex_lock(&hLock->m_mutex) != 0)
138 return FALSE; // Problem with mutex
139
140 if ((hLock->m_iRefCount == -1) || (hLock->m_dwWaitWriters > 0))
141 {
142 // Object is locked for writing or somebody wish to lock it for writing
143 hLock->m_dwWaitReaders++;
144
145 if (dwTimeOut == INFINITE)
146 {
147 retcode = pthread_cond_wait(&hLock->m_condRead, &hLock->m_mutex);
148 }
149 else
150 {
151 #if HAVE_PTHREAD_COND_RELTIMEDWAIT_NP
152 struct timespec timeout;
153
154 timeout.tv_sec = dwTimeOut / 1000;
155 timeout.tv_nsec = (dwTimeOut % 1000) * 1000000;
156 retcode = pthread_cond_reltimedwait_np(&hLock->m_condRead, &hLock->m_mutex, &timeout);
157 #else
158 struct timeval now;
159 struct timespec timeout;
160
161 gettimeofday(&now, NULL);
162 timeout.tv_sec = now.tv_sec + (dwTimeOut / 1000);
163 timeout.tv_nsec = ( now.tv_usec + ( dwTimeOut % 1000 ) * 1000) * 1000;
164 retcode = pthread_cond_timedwait(&hLock->m_condRead, &hLock->m_mutex, &timeout);
165 #endif
166 }
167
168 hLock->m_dwWaitReaders--;
169 if (retcode == 0)
170 {
171 assert(hLock->m_iRefCount != -1);
172 hLock->m_iRefCount++;
173 bResult = TRUE;
174 }
175 }
176 else
177 {
178 hLock->m_iRefCount++;
179 bResult = TRUE;
180 }
181
182 pthread_mutex_unlock(&hLock->m_mutex);
183 #endif
184
185 return bResult;
186 }
187
188
189 //
190 // Lock read/write lock for writing
191 //
192
193 BOOL LIBNETXMS_EXPORTABLE RWLockWriteLock(RWLOCK hLock, DWORD dwTimeOut)
194 {
195 BOOL bResult = FALSE;
196 int retcode;
197
198 // Check if handle is valid
199 if (hLock == NULL)
200 return FALSE;
201
202 #ifdef _WIN32
203 DWORD dwStart, dwElapsed;
204 BOOL bTimeOut = FALSE;
205
206 WaitForSingleObject(hLock->m_mutex, INFINITE);
207 // Reset reader event because it can be set by previous Unlock() call
208 ResetEvent(hLock->m_condRead);
209
210 do
211 {
212 if (hLock->m_iRefCount != 0)
213 {
214 hLock->m_dwWaitWriters++;
215 ReleaseMutex(hLock->m_mutex);
216 dwStart = GetTickCount();
217 retcode = WaitForSingleObject(hLock->m_condWrite, dwTimeOut);
218 dwElapsed = GetTickCount() - dwStart;
219 WaitForSingleObject(hLock->m_mutex, INFINITE); // Re-acquire mutex
220 hLock->m_dwWaitWriters--;
221 if (retcode == WAIT_TIMEOUT)
222 {
223 bTimeOut = TRUE;
224 }
225 else
226 {
227 if (dwTimeOut != INFINITE)
228 {
229 dwTimeOut -= min(dwElapsed, dwTimeOut);
230 }
231 }
232 }
233 else
234 {
235 hLock->m_iRefCount--;
236 bResult = TRUE;
237 }
238 } while((!bResult) && (!bTimeOut));
239
240 ReleaseMutex(hLock->m_mutex);
241 #else
242 if (pthread_mutex_lock(&hLock->m_mutex) != 0)
243 return FALSE; // Problem with mutex
244
245 if (hLock->m_iRefCount != 0)
246 {
247 // Object is locked, wait for unlock
248 hLock->m_dwWaitWriters++;
249
250 if (dwTimeOut == INFINITE)
251 {
252 retcode = pthread_cond_wait(&hLock->m_condWrite, &hLock->m_mutex);
253 }
254 else
255 {
256 #if HAVE_PTHREAD_COND_RELTIMEDWAIT_NP
257 struct timespec timeout;
258
259 timeout.tv_sec = dwTimeOut / 1000;
260 timeout.tv_nsec = (dwTimeOut % 1000) * 1000000;
261 retcode = pthread_cond_reltimedwait_np(&hLock->m_condWrite, &hLock->m_mutex, &timeout);
262 #else
263 struct timeval now;
264 struct timespec timeout;
265
266 gettimeofday(&now, NULL);
267 timeout.tv_sec = now.tv_sec + (dwTimeOut / 1000);
268 timeout.tv_nsec = ( now.tv_usec + ( dwTimeOut % 1000 ) * 1000) * 1000;
269 retcode = pthread_cond_timedwait(&hLock->m_condWrite, &hLock->m_mutex, &timeout);
270 #endif
271 }
272
273 hLock->m_dwWaitWriters--;
274 if (retcode == 0)
275 {
276 assert(hLock->m_iRefCount == 0);
277 hLock->m_iRefCount--;
278 bResult = TRUE;
279 }
280 }
281 else
282 {
283 hLock->m_iRefCount--;
284 bResult = TRUE;
285 }
286
287 pthread_mutex_unlock(&hLock->m_mutex);
288 #endif
289
290 return bResult;
291 }
292
293
294 //
295 // Unlock read/write lock
296 //
297
298 void LIBNETXMS_EXPORTABLE RWLockUnlock(RWLOCK hLock)
299 {
300 // Check if handle is valid
301 if (hLock == NULL)
302 return;
303
304 // Acquire access to handle
305 #ifdef _WIN32
306 WaitForSingleObject(hLock->m_mutex, INFINITE);
307 #else
308 if (pthread_mutex_lock(&hLock->m_mutex) != 0)
309 return; // Problem with mutex
310 #endif
311
312 // Remove lock
313 if (hLock->m_iRefCount > 0)
314 hLock->m_iRefCount--;
315 else if (hLock->m_iRefCount == -1)
316 hLock->m_iRefCount = 0;
317
318 // Notify waiting threads
319 if (hLock->m_dwWaitWriters > 0)
320 {
321 if (hLock->m_iRefCount == 0)
322 #ifdef _WIN32
323 SetEvent(hLock->m_condWrite);
324 #else
325 pthread_cond_signal(&hLock->m_condWrite);
326 #endif
327 }
328 else if (hLock->m_dwWaitReaders > 0)
329 {
330 #ifdef _WIN32
331 SetEvent(hLock->m_condRead);
332 #else
333 pthread_cond_broadcast(&hLock->m_condRead);
334 #endif
335 }
336
337 #ifdef _WIN32
338 ReleaseMutex(hLock->m_mutex);
339 #else
340 pthread_mutex_unlock(&hLock->m_mutex);
341 #endif
342 }
343
344 #endif /* !HAVE_PTHREAD_RWLOCK */