/[pcsx2_0.9.7]/trunk/3rdparty/w32pthreads/pthread_cond_wait.c
ViewVC logotype

Contents of /trunk/3rdparty/w32pthreads/pthread_cond_wait.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 31 - (show annotations) (download)
Tue Sep 7 03:24:11 2010 UTC (9 years, 11 months ago) by william
File MIME type: text/plain
File size: 16770 byte(s)
committing r3113 initial commit again...
1 /*
2 * pthread_cond_wait.c
3 *
4 * Description:
5 * This translation unit implements condition variables and their primitives.
6 *
7 *
8 * --------------------------------------------------------------------------
9 *
10 * Pthreads-win32 - POSIX Threads Library for Win32
11 * Copyright(C) 1998 John E. Bossom
12 * Copyright(C) 1999,2005 Pthreads-win32 contributors
13 *
14 * Contact Email: rpj@callisto.canberra.edu.au
15 *
16 * The current list of contributors is contained
17 * in the file CONTRIBUTORS included with the source
18 * code distribution. The list can also be seen at the
19 * following World Wide Web location:
20 * http://sources.redhat.com/pthreads-win32/contributors.html
21 *
22 * This library is free software; you can redistribute it and/or
23 * modify it under the terms of the GNU Lesser General Public
24 * License as published by the Free Software Foundation; either
25 * version 2 of the License, or (at your option) any later version.
26 *
27 * This library is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
30 * Lesser General Public License for more details.
31 *
32 * You should have received a copy of the GNU Lesser General Public
33 * License along with this library in the file COPYING.LIB;
34 * if not, write to the Free Software Foundation, Inc.,
35 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
36 *
37 * -------------------------------------------------------------
38 * Algorithm:
39 * The algorithm used in this implementation is that developed by
40 * Alexander Terekhov in colaboration with Louis Thomas. The bulk
41 * of the discussion is recorded in the file README.CV, which contains
42 * several generations of both colaborators original algorithms. The final
43 * algorithm used here is the one referred to as
44 *
45 * Algorithm 8a / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
46 *
47 * presented below in pseudo-code as it appeared:
48 *
49 *
50 * given:
51 * semBlockLock - bin.semaphore
52 * semBlockQueue - semaphore
53 * mtxExternal - mutex or CS
54 * mtxUnblockLock - mutex or CS
55 * nWaitersGone - int
56 * nWaitersBlocked - int
57 * nWaitersToUnblock - int
58 *
59 * wait( timeout ) {
60 *
61 * [auto: register int result ] // error checking omitted
62 * [auto: register int nSignalsWasLeft ]
63 * [auto: register int nWaitersWasGone ]
64 *
65 * sem_wait( semBlockLock );
66 * nWaitersBlocked++;
67 * sem_post( semBlockLock );
68 *
69 * unlock( mtxExternal );
70 * bTimedOut = sem_wait( semBlockQueue,timeout );
71 *
72 * lock( mtxUnblockLock );
73 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
74 * if ( bTimeout ) { // timeout (or canceled)
75 * if ( 0 != nWaitersBlocked ) {
76 * nWaitersBlocked--;
77 * }
78 * else {
79 * nWaitersGone++; // count spurious wakeups.
80 * }
81 * }
82 * if ( 0 == --nWaitersToUnblock ) {
83 * if ( 0 != nWaitersBlocked ) {
84 * sem_post( semBlockLock ); // open the gate.
85 * nSignalsWasLeft = 0; // do not open the gate
86 * // below again.
87 * }
88 * else if ( 0 != (nWaitersWasGone = nWaitersGone) ) {
89 * nWaitersGone = 0;
90 * }
91 * }
92 * }
93 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
94 * // spurious semaphore :-)
95 * sem_wait( semBlockLock );
96 * nWaitersBlocked -= nWaitersGone; // something is going on here
97 * // - test of timeouts? :-)
98 * sem_post( semBlockLock );
99 * nWaitersGone = 0;
100 * }
101 * unlock( mtxUnblockLock );
102 *
103 * if ( 1 == nSignalsWasLeft ) {
104 * if ( 0 != nWaitersWasGone ) {
105 * // sem_adjust( semBlockQueue,-nWaitersWasGone );
106 * while ( nWaitersWasGone-- ) {
107 * sem_wait( semBlockQueue ); // better now than spurious later
108 * }
109 * } sem_post( semBlockLock ); // open the gate
110 * }
111 *
112 * lock( mtxExternal );
113 *
114 * return ( bTimedOut ) ? ETIMEOUT : 0;
115 * }
116 *
117 * signal(bAll) {
118 *
119 * [auto: register int result ]
120 * [auto: register int nSignalsToIssue]
121 *
122 * lock( mtxUnblockLock );
123 *
124 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
125 * if ( 0 == nWaitersBlocked ) { // NO-OP
126 * return unlock( mtxUnblockLock );
127 * }
128 * if (bAll) {
129 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
130 * nWaitersBlocked = 0;
131 * }
132 * else {
133 * nSignalsToIssue = 1;
134 * nWaitersToUnblock++;
135 * nWaitersBlocked--;
136 * }
137 * }
138 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
139 * sem_wait( semBlockLock ); // close the gate
140 * if ( 0 != nWaitersGone ) {
141 * nWaitersBlocked -= nWaitersGone;
142 * nWaitersGone = 0;
143 * }
144 * if (bAll) {
145 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
146 * nWaitersBlocked = 0;
147 * }
148 * else {
149 * nSignalsToIssue = nWaitersToUnblock = 1;
150 * nWaitersBlocked--;
151 * }
152 * }
153 * else { // NO-OP
154 * return unlock( mtxUnblockLock );
155 * }
156 *
157 * unlock( mtxUnblockLock );
158 * sem_post( semBlockQueue,nSignalsToIssue );
159 * return result;
160 * }
161 * -------------------------------------------------------------
162 *
163 * Algorithm 9 / IMPL_SEM,UNBLOCK_STRATEGY == UNBLOCK_ALL
164 *
165 * presented below in pseudo-code; basically 8a...
166 * ...BUT W/O "spurious wakes" prevention:
167 *
168 *
169 * given:
170 * semBlockLock - bin.semaphore
171 * semBlockQueue - semaphore
172 * mtxExternal - mutex or CS
173 * mtxUnblockLock - mutex or CS
174 * nWaitersGone - int
175 * nWaitersBlocked - int
176 * nWaitersToUnblock - int
177 *
178 * wait( timeout ) {
179 *
180 * [auto: register int result ] // error checking omitted
181 * [auto: register int nSignalsWasLeft ]
182 *
183 * sem_wait( semBlockLock );
184 * ++nWaitersBlocked;
185 * sem_post( semBlockLock );
186 *
187 * unlock( mtxExternal );
188 * bTimedOut = sem_wait( semBlockQueue,timeout );
189 *
190 * lock( mtxUnblockLock );
191 * if ( 0 != (nSignalsWasLeft = nWaitersToUnblock) ) {
192 * --nWaitersToUnblock;
193 * }
194 * else if ( INT_MAX/2 == ++nWaitersGone ) { // timeout/canceled or
195 * // spurious semaphore :-)
196 * sem_wait( semBlockLock );
197 * nWaitersBlocked -= nWaitersGone; // something is going on here
198 * // - test of timeouts? :-)
199 * sem_post( semBlockLock );
200 * nWaitersGone = 0;
201 * }
202 * unlock( mtxUnblockLock );
203 *
204 * if ( 1 == nSignalsWasLeft ) {
205 * sem_post( semBlockLock ); // open the gate
206 * }
207 *
208 * lock( mtxExternal );
209 *
210 * return ( bTimedOut ) ? ETIMEOUT : 0;
211 * }
212 *
213 * signal(bAll) {
214 *
215 * [auto: register int result ]
216 * [auto: register int nSignalsToIssue]
217 *
218 * lock( mtxUnblockLock );
219 *
220 * if ( 0 != nWaitersToUnblock ) { // the gate is closed!!!
221 * if ( 0 == nWaitersBlocked ) { // NO-OP
222 * return unlock( mtxUnblockLock );
223 * }
224 * if (bAll) {
225 * nWaitersToUnblock += nSignalsToIssue=nWaitersBlocked;
226 * nWaitersBlocked = 0;
227 * }
228 * else {
229 * nSignalsToIssue = 1;
230 * ++nWaitersToUnblock;
231 * --nWaitersBlocked;
232 * }
233 * }
234 * else if ( nWaitersBlocked > nWaitersGone ) { // HARMLESS RACE CONDITION!
235 * sem_wait( semBlockLock ); // close the gate
236 * if ( 0 != nWaitersGone ) {
237 * nWaitersBlocked -= nWaitersGone;
238 * nWaitersGone = 0;
239 * }
240 * if (bAll) {
241 * nSignalsToIssue = nWaitersToUnblock = nWaitersBlocked;
242 * nWaitersBlocked = 0;
243 * }
244 * else {
245 * nSignalsToIssue = nWaitersToUnblock = 1;
246 * --nWaitersBlocked;
247 * }
248 * }
249 * else { // NO-OP
250 * return unlock( mtxUnblockLock );
251 * }
252 *
253 * unlock( mtxUnblockLock );
254 * sem_post( semBlockQueue,nSignalsToIssue );
255 * return result;
256 * }
257 * -------------------------------------------------------------
258 *
259 */
260
261 #include "ptw32pch.h"
262
263 /*
264 * Arguments for cond_wait_cleanup, since we can only pass a
265 * single void * to it.
266 */
267 typedef struct
268 {
269 pthread_mutex_t *mutexPtr;
270 pthread_cond_t cv;
271 int *resultPtr;
272 } ptw32_cond_wait_cleanup_args_t;
273
274 static void PTW32_CDECL
275 ptw32_cond_wait_cleanup (void *args)
276 {
277 ptw32_cond_wait_cleanup_args_t *cleanup_args =
278 (ptw32_cond_wait_cleanup_args_t *) args;
279 pthread_cond_t cv = cleanup_args->cv;
280 int *resultPtr = cleanup_args->resultPtr;
281 int nSignalsWasLeft;
282 int result;
283
284 /*
285 * Whether we got here as a result of signal/broadcast or because of
286 * timeout on wait or thread cancellation we indicate that we are no
287 * longer waiting. The waiter is responsible for adjusting waiters
288 * (to)unblock(ed) counts (protected by unblock lock).
289 */
290 if ((result = pthread_mutex_lock (&(cv->mtxUnblockLock))) != 0)
291 {
292 *resultPtr = result;
293 return;
294 }
295
296 if (0 != (nSignalsWasLeft = cv->nWaitersToUnblock))
297 {
298 --(cv->nWaitersToUnblock);
299 }
300 else if (INT_MAX / 2 == ++(cv->nWaitersGone))
301 {
302 /* Use the non-cancellable version of sem_wait() */
303 if (ptw32_semwait (&(cv->semBlockLock)) != 0)
304 {
305 *resultPtr = errno;
306 /*
307 * This is a fatal error for this CV,
308 * so we deliberately don't unlock
309 * cv->mtxUnblockLock before returning.
310 */
311 return;
312 }
313 cv->nWaitersBlocked -= cv->nWaitersGone;
314 if (sem_post (&(cv->semBlockLock)) != 0)
315 {
316 *resultPtr = errno;
317 /*
318 * This is a fatal error for this CV,
319 * so we deliberately don't unlock
320 * cv->mtxUnblockLock before returning.
321 */
322 return;
323 }
324 cv->nWaitersGone = 0;
325 }
326
327 if ((result = pthread_mutex_unlock (&(cv->mtxUnblockLock))) != 0)
328 {
329 *resultPtr = result;
330 return;
331 }
332
333 if (1 == nSignalsWasLeft)
334 {
335 if (sem_post (&(cv->semBlockLock)) != 0)
336 {
337 *resultPtr = errno;
338 return;
339 }
340 }
341
342 /*
343 * XSH: Upon successful return, the mutex has been locked and is owned
344 * by the calling thread.
345 */
346 if ((result = pthread_mutex_lock (cleanup_args->mutexPtr)) != 0)
347 {
348 *resultPtr = result;
349 }
350 } /* ptw32_cond_wait_cleanup */
351
352 static INLINE int
353 ptw32_cond_timedwait (pthread_cond_t * cond,
354 pthread_mutex_t * mutex, const struct timespec *abstime)
355 {
356 int result = 0;
357 pthread_cond_t cv;
358 ptw32_cond_wait_cleanup_args_t cleanup_args;
359
360 if (cond == NULL || *cond == NULL)
361 {
362 return EINVAL;
363 }
364
365 /*
366 * We do a quick check to see if we need to do more work
367 * to initialise a static condition variable. We check
368 * again inside the guarded section of ptw32_cond_check_need_init()
369 * to avoid race conditions.
370 */
371 if (*cond == PTHREAD_COND_INITIALIZER)
372 {
373 result = ptw32_cond_check_need_init (cond);
374 }
375
376 if (result != 0 && result != EBUSY)
377 {
378 return result;
379 }
380
381 cv = *cond;
382
383 /* Thread can be cancelled in sem_wait() but this is OK */
384 if (sem_wait (&(cv->semBlockLock)) != 0)
385 {
386 return errno;
387 }
388
389 ++(cv->nWaitersBlocked);
390
391 if (sem_post (&(cv->semBlockLock)) != 0)
392 {
393 return errno;
394 }
395
396 /*
397 * Setup this waiter cleanup handler
398 */
399 cleanup_args.mutexPtr = mutex;
400 cleanup_args.cv = cv;
401 cleanup_args.resultPtr = &result;
402
403 #ifdef _MSC_VER
404 #pragma inline_depth(0)
405 #endif
406 pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args);
407
408 /*
409 * Now we can release 'mutex' and...
410 */
411 if ((result = pthread_mutex_unlock (mutex)) == 0)
412 {
413
414 /*
415 * ...wait to be awakened by
416 * pthread_cond_signal, or
417 * pthread_cond_broadcast, or
418 * timeout, or
419 * thread cancellation
420 *
421 * Note:
422 *
423 * sem_timedwait is a cancellation point,
424 * hence providing the mechanism for making
425 * pthread_cond_wait a cancellation point.
426 * We use the cleanup mechanism to ensure we
427 * re-lock the mutex and adjust (to)unblock(ed) waiters
428 * counts if we are cancelled, timed out or signalled.
429 */
430 if (sem_timedwait (&(cv->semBlockQueue), abstime) != 0)
431 {
432 result = errno;
433 }
434 }
435
436 /*
437 * Always cleanup
438 */
439 pthread_cleanup_pop (1);
440 #ifdef _MSC_VER
441 #pragma inline_depth()
442 #endif
443
444 /*
445 * "result" can be modified by the cleanup handler.
446 */
447 return result;
448
449 } /* ptw32_cond_timedwait */
450
451
452 int
453 pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex)
454 /*
455 * ------------------------------------------------------
456 * DOCPUBLIC
457 * This function waits on a condition variable until
458 * awakened by a signal or broadcast.
459 *
460 * Caller MUST be holding the mutex lock; the
461 * lock is released and the caller is blocked waiting
462 * on 'cond'. When 'cond' is signaled, the mutex
463 * is re-acquired before returning to the caller.
464 *
465 * PARAMETERS
466 * cond
467 * pointer to an instance of pthread_cond_t
468 *
469 * mutex
470 * pointer to an instance of pthread_mutex_t
471 *
472 *
473 * DESCRIPTION
474 * This function waits on a condition variable until
475 * awakened by a signal or broadcast.
476 *
477 * NOTES:
478 *
479 * 1) The function must be called with 'mutex' LOCKED
480 * by the calling thread, or undefined behaviour
481 * will result.
482 *
483 * 2) This routine atomically releases 'mutex' and causes
484 * the calling thread to block on the condition variable.
485 * The blocked thread may be awakened by
486 * pthread_cond_signal or
487 * pthread_cond_broadcast.
488 *
489 * Upon successful completion, the 'mutex' has been locked and
490 * is owned by the calling thread.
491 *
492 *
493 * RESULTS
494 * 0 caught condition; mutex released,
495 * EINVAL 'cond' or 'mutex' is invalid,
496 * EINVAL different mutexes for concurrent waits,
497 * EINVAL mutex is not held by the calling thread,
498 *
499 * ------------------------------------------------------
500 */
501 {
502 /*
503 * The NULL abstime arg means INFINITE waiting.
504 */
505 return (ptw32_cond_timedwait (cond, mutex, NULL));
506
507 } /* pthread_cond_wait */
508
509
510 int
511 pthread_cond_timedwait (pthread_cond_t * cond,
512 pthread_mutex_t * mutex,
513 const struct timespec *abstime)
514 /*
515 * ------------------------------------------------------
516 * DOCPUBLIC
517 * This function waits on a condition variable either until
518 * awakened by a signal or broadcast; or until the time
519 * specified by abstime passes.
520 *
521 * PARAMETERS
522 * cond
523 * pointer to an instance of pthread_cond_t
524 *
525 * mutex
526 * pointer to an instance of pthread_mutex_t
527 *
528 * abstime
529 * pointer to an instance of (const struct timespec)
530 *
531 *
532 * DESCRIPTION
533 * This function waits on a condition variable either until
534 * awakened by a signal or broadcast; or until the time
535 * specified by abstime passes.
536 *
537 * NOTES:
538 * 1) The function must be called with 'mutex' LOCKED
539 * by the calling thread, or undefined behaviour
540 * will result.
541 *
542 * 2) This routine atomically releases 'mutex' and causes
543 * the calling thread to block on the condition variable.
544 * The blocked thread may be awakened by
545 * pthread_cond_signal or
546 * pthread_cond_broadcast.
547 *
548 *
549 * RESULTS
550 * 0 caught condition; mutex released,
551 * EINVAL 'cond', 'mutex', or abstime is invalid,
552 * EINVAL different mutexes for concurrent waits,
553 * EINVAL mutex is not held by the calling thread,
554 * ETIMEDOUT abstime ellapsed before cond was signaled.
555 *
556 * ------------------------------------------------------
557 */
558 {
559 if (abstime == NULL)
560 {
561 return EINVAL;
562 }
563
564 return (ptw32_cond_timedwait (cond, mutex, abstime));
565
566 } /* pthread_cond_timedwait */

  ViewVC Help
Powered by ViewVC 1.1.22