1 /*
2 * Copyright 2006-2008 The FLWOR Foundation.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 #include "stdafx.h"
17
18 #include "api/zorbaimpl.h"
19
20 #include "zorbautils/fatal.h"
21 #include "zorbautils/latch.h"
22 #include <cassert>
23 #ifdef ZORBA_HAVE_PTHREAD_H
24 # include <pthread.h>
25 #endif
26
27
28 namespace zorba {
29
30
31 #ifndef ZORBA_FOR_ONE_THREAD_ONLY
32
33
34 /////////////////////////////////////////////////////////////////////////////////
35 // //
36 // pthread Latch //
37 // //
38 /////////////////////////////////////////////////////////////////////////////////
39
40 #ifdef ZORBA_HAVE_PTHREAD_H
41
42
Latch()43 Latch::Latch()
44 {
45 pthread_rwlockattr_t attr;
46 pthread_rwlockattr_init(&attr);
47
48 #ifndef NDEBUG
49 int pshared;
50 pthread_rwlockattr_getpshared(&attr, &pshared);
51 ZORBA_FATAL(pshared == PTHREAD_PROCESS_PRIVATE,
52 "rwlock pshared = " << pshared);
53 #endif
54
55 pthread_rwlock_init(&theLatch, &attr);
56
57 pthread_rwlockattr_destroy(&attr);
58 }
59
60
61
~Latch()62 Latch::~Latch()
63 {
64 pthread_rwlock_destroy(&theLatch);
65 }
66
67
rlock()68 void Latch::rlock()
69 {
70 int ret= pthread_rwlock_rdlock(&theLatch);
71 ZORBA_FATAL(!ret, "Failed to acquire latch. Error code = " << ret);
72 }
73
74
wlock()75 void Latch::wlock()
76 {
77 int ret = pthread_rwlock_wrlock(&theLatch);
78 ZORBA_FATAL(!ret, "Failed to acquire latch. Error code = " << ret);
79 }
80
81
unlock()82 void Latch::unlock()
83 {
84 int ret= pthread_rwlock_unlock(&theLatch);
85 ZORBA_FATAL(!ret, "Failed to release latch. Error code = " << ret);
86 }
87
88
89 /////////////////////////////////////////////////////////////////////////////////
90 // //
91 // WIN32 Latch //
92 // //
93 /////////////////////////////////////////////////////////////////////////////////
94
95
96 #elif defined WIN32
97 #include <errno.h>
98
99 Latch::Latch()
100 :
101 valid(LATCH_INVALID),
102 r_active(0),
103 w_active(0),
104 r_wait(0),
105 w_wait(0),
106 rlocked(false),
107 wlocked(false)
108 {
109 mutex = CreateEvent(NULL, FALSE, TRUE, NULL);
110 assert(mutex != NULL);
111 if(mutex == NULL)
112 return;
113
114 cond_read = CreateEvent(NULL, FALSE, FALSE, NULL);
115 assert(cond_read != NULL);
116 if(cond_read == NULL)
117 {
118 CloseHandle(mutex);
119 return;
120 }
121
122 cond_write = CreateEvent(NULL, FALSE, FALSE, NULL);
123 assert(cond_write != NULL);
124 if(cond_read == NULL)
125 {
126 CloseHandle(mutex);
127 CloseHandle(cond_read);
128 return;
129 }
130
131 valid = LATCH_VALID;
132 }
133
134
135 Latch::~Latch()
136 {
137 destroy();
138 }
139
140 void Latch::rlock()
141 {
142 readlock();
143 rlocked = true;
144 }
145
146 void Latch::wlock()
147 {
148 writelock();
149 wlocked = true;
150 }
151
152 void Latch::unlock()
153 {
154 if(wlocked)
155 writeunlock();
156
157 if(rlocked)
158 readunlock();
159
160 rlocked = false;
161 wlocked = false;
162 }
163
164
165 int Latch::lock_mutex()
166 {
167 // return 0 for success
168
169 if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0)
170 return 1;
171
172 return 0;
173 }
174
175
176 int Latch::unlock_mutex()
177 {
178 // return 0 for success
179 return !SetEvent(mutex);
180 }
181
182
183 int Latch::signal_cond_read()
184 {
185 return !SetEvent(cond_read);
186 }
187
188
189 int Latch::broadcast_cond_read()
190 {
191 return !PulseEvent(cond_read);
192 }
193
194
195 int Latch::wait_cond_read()
196 {
197 DWORD retwait;
198 unlock_mutex();
199 retwait = WaitForSingleObject(cond_read, INFINITE);
200 lock_mutex();
201 return retwait != WAIT_OBJECT_0;
202 }
203
204
205 int Latch::signal_cond_write()
206 {
207 return !SetEvent(cond_write);
208 }
209
210
211 int Latch::broadcast_cond_write()
212 {
213 return !PulseEvent(cond_write);
214 }
215
216
217 int Latch::wait_cond_write()
218 {
219 DWORD retwait;
220 unlock_mutex();
221 retwait = WaitForSingleObject(cond_write, INFINITE);
222 lock_mutex();
223 return retwait != WAIT_OBJECT_0;
224 }
225
226
227 /*_____________________________________________________________
228 |
229 | Destroy a read/write lock.
230 |______________________________________________________________*/
231
232 int Latch::destroy()
233 {
234 int status;
235
236 if (valid != LATCH_VALID) return EINVAL;
237
238 status = lock_mutex();
239 if (status!=0) return status;
240
241 // check if any threads own the lock, report "busy" if so.
242 if (r_active > 0 || w_active) {
243 unlock_mutex();
244 return EBUSY;
245 }
246
247 // check if any threads are known to be waiting, report "busy" if so
248 if (r_wait > 0 || w_wait > 0) {
249 unlock_mutex();
250 return EBUSY;
251 }
252
253 valid = LATCH_INVALID;
254 status = unlock_mutex();
255 if (status!=0) return status;
256
257 CloseHandle(mutex);
258 CloseHandle(cond_read);
259 CloseHandle(cond_write);
260 return 0;
261 }
262
263
264 /*******************************************************************************
265 Lock a read/write lock for read access.
266
267 "Read preference" expressed by waiting only for active writers, as opposed
268 to waiting writers.
269 ********************************************************************************/
270 int Latch::readlock()
271 {
272 #ifdef WIN32
273 if(ZorbaImpl::ctrl_c_signaled)
274 return 0;
275 #endif
276 int status;
277
278 if (valid != LATCH_VALID) return EINVAL;
279
280 if ( (status = lock_mutex()) != 0) return status;
281
282 if (w_active)
283 {
284 // writer is active
285 r_wait++;
286
287 // wait on read condition variable broadcast by writer
288 while (w_active) {
289 status = wait_cond_read();
290 if (status!=0) break;
291 }
292
293 r_wait--;
294 }
295
296 if (status==0) r_active++;
297
298 unlock_mutex();
299 return status;
300 }
301
302
303 /*******************************************************************************
304 Attempt to lock a read/write lock for read access, but don't block if
305 unavailable.
306 ********************************************************************************/
307 int Latch::readtrylock()
308 {
309 #ifdef WIN32
310 if(ZorbaImpl::ctrl_c_signaled)
311 return 0;
312 #endif
313 int status, status2;
314
315 if (valid != LATCH_VALID) return EINVAL;
316
317 if ( (status = lock_mutex()) != 0) return status;
318
319 if (w_active)
320 status = EBUSY;
321 else
322 r_active++;
323
324 status2 = unlock_mutex();
325 return (status2 != 0 ? status2 : status);
326 }
327
328
329 /*******************************************************************************
330 Unlock a read/write lock from read access.
331
332 If no more active readers, and at least one waiting writer, signal the write
333 condition variable. Race for access may select readlock or readtrylock.
334 ********************************************************************************/
335 int Latch::readunlock()
336 {
337 #ifdef WIN32
338 if(ZorbaImpl::ctrl_c_signaled)
339 return 0;
340 #endif
341 int status, status2;
342
343 if (valid != LATCH_VALID) return EINVAL;
344
345 if ( (status = lock_mutex()) != 0) return status;
346
347 r_active--;
348 if (r_active == 0 && w_wait > 0)
349 status = signal_cond_write();
350
351 status2 = unlock_mutex();
352 return (status2==0 ? status : status2);
353 }
354
355
356 /*******************************************************************************
357 Lock a read/write lock for write access.
358 ********************************************************************************/
359 int Latch::writelock()
360 {
361 #ifdef WIN32
362 if(ZorbaImpl::ctrl_c_signaled)
363 return 0;
364 #endif
365 int status;
366
367 if (valid != LATCH_VALID) return EINVAL;
368
369 if ( (status = lock_mutex()) != 0) return status;
370
371 if (w_active || r_active > 0)
372 {
373 w_wait++;
374
375 while (w_active || r_active > 0)
376 {
377 if ( (status = wait_cond_write()) != 0) break;
378 }
379
380 w_wait--;
381 }
382
383 if (status==0) w_active = 1;
384
385 unlock_mutex();
386 return status;
387 }
388
389
390 int Latch::writetrylock()
391 {
392 #ifdef WIN32
393 if(ZorbaImpl::ctrl_c_signaled)
394 return 0;
395 #endif
396 int status, status2;
397
398 if (valid != LATCH_VALID) return EINVAL;
399
400 if ( (status = lock_mutex()) != 0) return status;
401
402 if (w_active || r_active > 0) {
403 status = EBUSY;
404 }
405 else {
406 w_active = 1;
407 }
408 status2 = unlock_mutex();
409 return (status != 0 ? status : status2);
410 }
411
412
413 int Latch::writeunlock()
414 {
415 #ifdef WIN32
416 if(ZorbaImpl::ctrl_c_signaled)
417 return 0;
418 #endif
419 int status;
420
421 if (valid != LATCH_VALID) return EINVAL;
422
423 if ( (status = lock_mutex()) != 0) return status;
424
425 w_active = 0;
426 // preferrentially wake up waiting readers
427 if (r_wait > 0)
428 {
429 if ( (status = broadcast_cond_read()) != 0)
430 {
431 unlock_mutex();
432 return status;
433 }
434 }
435 // if no waiting readers, look for waiting writers
436 else if (w_wait > 0)
437 {
438 if ( (status = signal_cond_write()) != 0)
439 {
440 unlock_mutex();
441 return status;
442 }
443 }
444
445 return unlock_mutex();
446 }
447
448 #endif
449
450
451 #endif
452
453 } // namespace zorba
454 /* vim:set et sw=2 ts=2: */
455