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