[BZ #163]
[platform/upstream/glibc.git] / nptl / sysdeps / pthread / pthread_cond_wait.c
1 /* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Martin Schwidefsky <schwidefsky@de.ibm.com>, 2003.
4
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9
10    The GNU C Library is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Lesser General Public License for more details.
14
15    You should have received a copy of the GNU Lesser General Public
16    License along with the GNU C Library; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <endian.h>
21 #include <errno.h>
22 #include <sysdep.h>
23 #include <lowlevellock.h>
24 #include <pthread.h>
25 #include <pthreadP.h>
26
27 #include <shlib-compat.h>
28
29
30 struct _condvar_cleanup_buffer
31 {
32   int oldtype;
33   pthread_cond_t *cond;
34   pthread_mutex_t *mutex;
35   unsigned int bc_seq;
36 };
37
38
39 void
40 __attribute__ ((visibility ("hidden")))
41 __condvar_cleanup (void *arg)
42 {
43   struct _condvar_cleanup_buffer *cbuffer =
44     (struct _condvar_cleanup_buffer *) arg;
45
46   /* We are going to modify shared data.  */
47   lll_mutex_lock (cbuffer->cond->__data.__lock);
48
49   if (cbuffer->bc_seq == cbuffer->cond->__data.__broadcast_seq)
50     {
51       /* This thread is not waiting anymore.  Adjust the sequence counters
52          appropriately.  */
53       ++cbuffer->cond->__data.__wakeup_seq;
54       ++cbuffer->cond->__data.__woken_seq;
55     }
56
57   /* We are done.  */
58   lll_mutex_unlock (cbuffer->cond->__data.__lock);
59
60   /* Wake everybody to make sure no condvar signal gets lost.  */
61 #if BYTE_ORDER == LITTLE_ENDIAN
62   int *futex = ((int *) (&cbuffer->cond->__data.__wakeup_seq));
63 #elif BYTE_ORDER == BIG_ENDIAN
64   int *futex = ((int *) (&cbuffer->cond->__data.__wakeup_seq)) + 1;
65 #else
66 # error "No valid byte order"
67 #endif
68   lll_futex_wake (futex, INT_MAX);
69
70   /* Get the mutex before returning unless asynchronous cancellation
71      is in effect.  */
72   __pthread_mutex_cond_lock (cbuffer->mutex);
73 }
74
75
76 int
77 __pthread_cond_wait (cond, mutex)
78      pthread_cond_t *cond;
79      pthread_mutex_t *mutex;
80 {
81   struct _pthread_cleanup_buffer buffer;
82   struct _condvar_cleanup_buffer cbuffer;
83   int err;
84
85   /* Make sure we are along.  */
86   lll_mutex_lock (cond->__data.__lock);
87
88   /* Now we can release the mutex.  */
89   err = __pthread_mutex_unlock_usercnt (mutex, 0);
90   if (__builtin_expect (err, 0))
91     {
92       lll_mutex_unlock (cond->__data.__lock);
93       return err;
94     }
95
96   /* We have one new user of the condvar.  */
97   ++cond->__data.__total_seq;
98
99   /* Remember the mutex we are using here.  If there is already a
100      different address store this is a bad user bug.  Do not store
101      anything for pshared condvars.  */
102   if (cond->__data.__mutex != (void *) ~0l)
103     cond->__data.__mutex = mutex;
104
105   /* Prepare structure passed to cancellation handler.  */
106   cbuffer.cond = cond;
107   cbuffer.mutex = mutex;
108
109   /* Before we block we enable cancellation.  Therefore we have to
110      install a cancellation handler.  */
111   __pthread_cleanup_push (&buffer, __condvar_cleanup, &cbuffer);
112
113   /* The current values of the wakeup counter.  The "woken" counter
114      must exceed this value.  */
115   unsigned long long int val;
116   unsigned long long int seq;
117   val = seq = cond->__data.__wakeup_seq;
118   /* Remember the broadcast counter.  */
119   cbuffer.bc_seq = cond->__data.__broadcast_seq;
120
121   /* The futex syscall operates on a 32-bit word.  That is fine, we
122      just use the low 32 bits of the sequence counter.  */
123 #if BYTE_ORDER == LITTLE_ENDIAN
124   int *futex = ((int *) (&cond->__data.__wakeup_seq));
125 #elif BYTE_ORDER == BIG_ENDIAN
126   int *futex = ((int *) (&cond->__data.__wakeup_seq)) + 1;
127 #else
128 # error "No valid byte order"
129 #endif
130
131   do
132     {
133       /* Prepare to wait.  Release the condvar futex.  */
134       lll_mutex_unlock (cond->__data.__lock);
135
136       /* Enable asynchronous cancellation.  Required by the standard.  */
137       cbuffer.oldtype = __pthread_enable_asynccancel ();
138
139       /* Wait until woken by signal or broadcast.  Note that we
140          truncate the 'val' value to 32 bits.  */
141       lll_futex_wait (futex, (unsigned int) val);
142
143       /* Disable asynchronous cancellation.  */
144       __pthread_disable_asynccancel (cbuffer.oldtype);
145
146       /* If a broadcast happened, we are done.  */
147       if (cbuffer.bc_seq != cond->__data.__broadcast_seq)
148         goto bc_out;
149
150       /* We are going to look at shared data again, so get the lock.  */
151       lll_mutex_lock (cond->__data.__lock);
152
153       /* Check whether we are eligible for wakeup.  */
154       val = cond->__data.__wakeup_seq;
155     }
156   while (val == seq || cond->__data.__woken_seq == val);
157
158   /* Another thread woken up.  */
159   ++cond->__data.__woken_seq;
160
161  bc_out:
162   /* We are done with the condvar.  */
163   lll_mutex_unlock (cond->__data.__lock);
164
165   /* The cancellation handling is back to normal, remove the handler.  */
166   __pthread_cleanup_pop (&buffer, 0);
167
168   /* Get the mutex before returning.  */
169   return __pthread_mutex_cond_lock (mutex);
170 }
171
172 versioned_symbol (libpthread, __pthread_cond_wait, pthread_cond_wait,
173                   GLIBC_2_3_2);