Handle spurious wakeups.
[kopensolaris-gnu/glibc.git] / linuxthreads / condvar.c
1 /* Linuxthreads - a simple clone()-based implementation of Posix        */
2 /* threads for Linux.                                                   */
3 /* Copyright (C) 1996 Xavier Leroy (Xavier.Leroy@inria.fr)              */
4 /* and Pavel Krauz (krauz@fsid.cvut.cz).                                */
5 /*                                                                      */
6 /* This program is free software; you can redistribute it and/or        */
7 /* modify it under the terms of the GNU Library General Public License  */
8 /* as published by the Free Software Foundation; either version 2       */
9 /* of the License, or (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 Library General Public License for more details.                 */
15
16 /* Condition variables */
17
18 #include <errno.h>
19 #include <sched.h>
20 #include <stddef.h>
21 #include <sys/time.h>
22 #include "pthread.h"
23 #include "internals.h"
24 #include "spinlock.h"
25 #include "queue.h"
26 #include "restart.h"
27
28 int pthread_cond_init(pthread_cond_t *cond,
29                       const pthread_condattr_t *cond_attr)
30 {
31   __pthread_init_lock(&cond->__c_lock);
32   cond->__c_waiting = NULL;
33   return 0;
34 }
35
36 int pthread_cond_destroy(pthread_cond_t *cond)
37 {
38   if (cond->__c_waiting != NULL) return EBUSY;
39   return 0;
40 }
41
42 /* Function called by pthread_cancel to remove the thread from
43    waiting on a condition variable queue. */
44
45 static int cond_extricate_func(void *obj, pthread_descr th)
46 {
47   volatile pthread_descr self = thread_self();
48   pthread_cond_t *cond = obj;
49   int did_remove = 0;
50
51   __pthread_lock(&cond->__c_lock, self);
52   did_remove = remove_from_queue(&cond->__c_waiting, th);
53   __pthread_unlock(&cond->__c_lock);
54
55   return did_remove;
56 }
57
58 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
59 {
60   volatile pthread_descr self = thread_self();
61   pthread_extricate_if extr;
62   int already_canceled = 0;
63   int spurious_wakeup_count;
64
65   /* Check whether the mutex is locked and owned by this thread.  */
66   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
67       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
68       && mutex->__m_owner != self)
69     return EINVAL;
70
71   /* Set up extrication interface */
72   extr.pu_object = cond;
73   extr.pu_extricate_func = cond_extricate_func;
74
75   /* Register extrication interface */
76   THREAD_SETMEM(self, p_condvar_avail, 0);
77   __pthread_set_own_extricate_if(self, &extr);
78
79   /* Atomically enqueue thread for waiting, but only if it is not
80      canceled. If the thread is canceled, then it will fall through the
81      suspend call below, and then call pthread_exit without
82      having to worry about whether it is still on the condition variable queue.
83      This depends on pthread_cancel setting p_canceled before calling the
84      extricate function. */
85
86   __pthread_lock(&cond->__c_lock, self);
87   if (!(THREAD_GETMEM(self, p_canceled)
88       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
89     enqueue(&cond->__c_waiting, self);
90   else
91     already_canceled = 1;
92   __pthread_unlock(&cond->__c_lock);
93
94   if (already_canceled) {
95     __pthread_set_own_extricate_if(self, 0);
96     pthread_exit(PTHREAD_CANCELED);
97   }
98
99   pthread_mutex_unlock(mutex);
100
101   spurious_wakeup_count = 0;
102   while (1)
103     {
104       suspend(self);
105       if (THREAD_GETMEM(self, p_condvar_avail) == 0
106           && THREAD_GETMEM(self, p_woken_by_cancel) == 0)
107         {
108           /* Count resumes that don't belong to us. */
109           spurious_wakeup_count++;
110           continue;
111         }
112       break;
113     }
114
115   __pthread_set_own_extricate_if(self, 0);
116
117   /* Check for cancellation again, to provide correct cancellation
118      point behavior */
119
120   if (THREAD_GETMEM(self, p_woken_by_cancel)
121       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
122     THREAD_SETMEM(self, p_woken_by_cancel, 0);
123     pthread_mutex_lock(mutex);
124     pthread_exit(PTHREAD_CANCELED);
125   }
126
127   /* Put back any resumes we caught that don't belong to us. */
128   while (spurious_wakeup_count--)
129     restart(self);
130
131   pthread_mutex_lock(mutex);
132   return 0;
133 }
134
135 static int
136 pthread_cond_timedwait_relative(pthread_cond_t *cond,
137                                 pthread_mutex_t *mutex,
138                                 const struct timespec * abstime)
139 {
140   volatile pthread_descr self = thread_self();
141   int already_canceled = 0;
142   pthread_extricate_if extr;
143   int spurious_wakeup_count;
144
145   /* Check whether the mutex is locked and owned by this thread.  */
146   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
147       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
148       && mutex->__m_owner != self)
149     return EINVAL;
150
151   /* Set up extrication interface */
152   extr.pu_object = cond;
153   extr.pu_extricate_func = cond_extricate_func;
154
155   /* Register extrication interface */
156   THREAD_SETMEM(self, p_condvar_avail, 0);
157   __pthread_set_own_extricate_if(self, &extr);
158
159   /* Enqueue to wait on the condition and check for cancellation. */
160   __pthread_lock(&cond->__c_lock, self);
161   if (!(THREAD_GETMEM(self, p_canceled)
162       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
163     enqueue(&cond->__c_waiting, self);
164   else
165     already_canceled = 1;
166   __pthread_unlock(&cond->__c_lock);
167
168   if (already_canceled) {
169     __pthread_set_own_extricate_if(self, 0);
170     pthread_exit(PTHREAD_CANCELED);
171   }
172
173   pthread_mutex_unlock(mutex);
174
175   spurious_wakeup_count = 0;
176   while (1)
177     {
178       if (!timedsuspend(self, abstime)) {
179         int was_on_queue;
180
181         /* __pthread_lock will queue back any spurious restarts that
182            may happen to it. */
183
184         __pthread_lock(&cond->__c_lock, self);
185         was_on_queue = remove_from_queue(&cond->__c_waiting, self);
186         __pthread_unlock(&cond->__c_lock);
187
188         if (was_on_queue) {
189           __pthread_set_own_extricate_if(self, 0);
190           pthread_mutex_lock(mutex);
191           return ETIMEDOUT;
192         }
193
194         /* Eat the outstanding restart() from the signaller */
195         suspend(self);
196       }
197
198       if (THREAD_GETMEM(self, p_condvar_avail) == 0
199           && THREAD_GETMEM(self, p_woken_by_cancel) == 0)
200         {
201           /* Count resumes that don't belong to us. */
202           spurious_wakeup_count++;
203           continue;
204         }
205       break;
206     }
207
208   __pthread_set_own_extricate_if(self, 0);
209
210   /* The remaining logic is the same as in other cancellable waits,
211      such as pthread_join sem_wait or pthread_cond wait. */
212
213   if (THREAD_GETMEM(self, p_woken_by_cancel)
214       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
215     THREAD_SETMEM(self, p_woken_by_cancel, 0);
216     pthread_mutex_lock(mutex);
217     pthread_exit(PTHREAD_CANCELED);
218   }
219
220   /* Put back any resumes we caught that don't belong to us. */
221   while (spurious_wakeup_count--)
222     restart(self);
223
224   pthread_mutex_lock(mutex);
225   return 0;
226 }
227
228 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
229                            const struct timespec * abstime)
230 {
231   /* Indirect call through pointer! */
232   return pthread_cond_timedwait_relative(cond, mutex, abstime);
233 }
234
235 int pthread_cond_signal(pthread_cond_t *cond)
236 {
237   pthread_descr th;
238
239   __pthread_lock(&cond->__c_lock, NULL);
240   th = dequeue(&cond->__c_waiting);
241   __pthread_unlock(&cond->__c_lock);
242   if (th != NULL) {
243     th->p_condvar_avail = 1;
244     WRITE_MEMORY_BARRIER();
245     restart(th);
246   }
247   return 0;
248 }
249
250 int pthread_cond_broadcast(pthread_cond_t *cond)
251 {
252   pthread_descr tosignal, th;
253
254   __pthread_lock(&cond->__c_lock, NULL);
255   /* Copy the current state of the waiting queue and empty it */
256   tosignal = cond->__c_waiting;
257   cond->__c_waiting = NULL;
258   __pthread_unlock(&cond->__c_lock);
259   /* Now signal each process in the queue */
260   while ((th = dequeue(&tosignal)) != NULL) {
261     th->p_condvar_avail = 1;
262     WRITE_MEMORY_BARRIER();
263     restart(th);
264   }
265   return 0;
266 }
267
268 int pthread_condattr_init(pthread_condattr_t *attr)
269 {
270   return 0;
271 }
272
273 int pthread_condattr_destroy(pthread_condattr_t *attr)
274 {
275   return 0;
276 }
277
278 int pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared)
279 {
280   *pshared = PTHREAD_PROCESS_PRIVATE;
281   return 0;
282 }
283
284 int pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared)
285 {
286   if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
287     return EINVAL;
288
289   /* For now it is not possible to shared a conditional variable.  */
290   if (pshared != PTHREAD_PROCESS_PRIVATE)
291     return ENOSYS;
292
293   return 0;
294 }