aab3ff257b20df76d4fdaeefb16fc6522c3cc173
[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 static int pthread_cond_timedwait_relative_old(pthread_cond_t *,
29     pthread_mutex_t *, struct timespec *);
30
31 static int pthread_cond_timedwait_relative_new(pthread_cond_t *,
32     pthread_mutex_t *, struct timespec *);
33
34 static int (*pthread_cond_tw_rel)(pthread_cond_t *, pthread_mutex_t *,
35     struct timespec *) = pthread_cond_timedwait_relative_old;
36
37 /* initialize this module */
38 void __pthread_init_condvar(int rt_sig_available)
39 {
40   if (rt_sig_available)
41     pthread_cond_tw_rel = pthread_cond_timedwait_relative_new;
42 }
43
44 int pthread_cond_init(pthread_cond_t *cond,
45                       const pthread_condattr_t *cond_attr)
46 {
47   __pthread_init_lock(&cond->__c_lock);
48   cond->__c_waiting = NULL;
49   return 0;
50 }
51
52 int pthread_cond_destroy(pthread_cond_t *cond)
53 {
54   if (cond->__c_waiting != NULL) return EBUSY;
55   return 0;
56 }
57
58 /* Function called by pthread_cancel to remove the thread from
59    waiting on a condition variable queue. */
60
61 static int cond_extricate_func(void *obj, pthread_descr th)
62 {
63   volatile pthread_descr self = thread_self();
64   pthread_cond_t *cond = obj;
65   int did_remove = 0;
66
67   __pthread_lock(&cond->__c_lock, self);
68   did_remove = remove_from_queue(&cond->__c_waiting, th);
69   __pthread_unlock(&cond->__c_lock);
70
71   return did_remove;
72 }
73
74 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
75 {
76   volatile pthread_descr self = thread_self();
77   pthread_extricate_if extr;
78   int already_canceled = 0;
79
80   /* Set up extrication interface */
81   extr.pu_object = cond;
82   extr.pu_extricate_func = cond_extricate_func;
83
84   /* Register extrication interface */
85   __pthread_set_own_extricate_if(self, &extr);
86
87   /* Atomically enqueue thread for waiting, but only if it is not
88      canceled. If the thread is canceled, then it will fall through the
89      suspend call below, and then call pthread_exit without
90      having to worry about whether it is still on the condition variable queue.
91      This depends on pthread_cancel setting p_canceled before calling the
92      extricate function. */
93
94   __pthread_lock(&cond->__c_lock, self);
95   if (!(THREAD_GETMEM(self, p_canceled)
96       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
97     enqueue(&cond->__c_waiting, self);
98   else
99     already_canceled = 1;
100   __pthread_unlock(&cond->__c_lock);
101
102   if (already_canceled) {
103     __pthread_set_own_extricate_if(self, 0);
104     pthread_exit(PTHREAD_CANCELED);
105   }
106
107   pthread_mutex_unlock(mutex);
108
109   suspend(self);
110   __pthread_set_own_extricate_if(self, 0);
111
112   /* Check for cancellation again, to provide correct cancellation
113      point behavior */
114
115   if (THREAD_GETMEM(self, p_woken_by_cancel)
116       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
117     THREAD_SETMEM(self, p_woken_by_cancel, 0);
118     pthread_mutex_lock(mutex);
119     pthread_exit(PTHREAD_CANCELED);
120   }
121
122   pthread_mutex_lock(mutex);
123   return 0;
124 }
125
126 /* The following function is used on kernels that don't have rt signals.
127    SIGUSR1 is used as the restart signal. The different code is needed
128    because that ordinary signal does not queue. */
129
130 static int
131 pthread_cond_timedwait_relative_old(pthread_cond_t *cond,
132                                 pthread_mutex_t *mutex,
133                                 struct timespec * reltime)
134 {
135   volatile pthread_descr self = thread_self();
136   sigset_t unblock, initial_mask;
137   int retsleep, already_canceled, was_signalled;
138   sigjmp_buf jmpbuf;
139   pthread_extricate_if extr;
140
141 requeue_and_wait_again:
142
143   retsleep = 0;
144   already_canceled = 0;
145   was_signalled = 0;
146
147   /* Set up extrication interface */
148   extr.pu_object = cond;
149   extr.pu_extricate_func = cond_extricate_func;
150
151   /* Register extrication interface */
152   __pthread_set_own_extricate_if(self, &extr);
153
154   /* Enqueue to wait on the condition and check for cancellation. */
155   __pthread_lock(&cond->__c_lock, self);
156   if (!(THREAD_GETMEM(self, p_canceled)
157       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
158     enqueue(&cond->__c_waiting, self);
159   else
160     already_canceled = 1;
161   __pthread_unlock(&cond->__c_lock);
162
163   if (already_canceled) {
164     __pthread_set_own_extricate_if(self, 0);
165     pthread_exit(PTHREAD_CANCELED);
166   }
167
168   pthread_mutex_unlock(mutex);
169
170   if (atomic_decrement(&self->p_resume_count) == 0) {
171     /* Set up a longjmp handler for the restart signal, unblock
172        the signal and sleep. */
173
174     if (sigsetjmp(jmpbuf, 1) == 0) {
175       THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
176       THREAD_SETMEM(self, p_signal, 0);
177       /* Unblock the restart signal */
178       sigemptyset(&unblock);
179       sigaddset(&unblock, __pthread_sig_restart);
180       sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
181       /* Sleep for the required duration */
182       retsleep = __libc_nanosleep(reltime, reltime);
183       /* Block the restart signal again */
184       sigprocmask(SIG_SETMASK, &initial_mask, NULL);
185       was_signalled = 0;
186     } else {
187       retsleep = -1;
188       was_signalled = 1;
189     }
190     THREAD_SETMEM(self, p_signal_jmp, NULL);
191   }
192
193   /* Now was_signalled is true if we exited the above code
194      due to the delivery of a restart signal.  In that case,
195      we know we have been dequeued and resumed and that the
196      resume count is balanced.  Otherwise, there are some
197      cases to consider. First, try to bump up the resume count
198      back to zero. If it goes to 1, it means restart() was
199      invoked on this thread. The signal must be consumed
200      and the count bumped down and everything is cool.
201      Otherwise, no restart was delivered yet, so we remove
202      the thread from the queue. If this succeeds, it's a clear
203      case of timeout. If we fail to remove from the queue, then we
204      must wait for a restart. */
205
206   if (!was_signalled) {
207     if (atomic_increment(&self->p_resume_count) != -1) {
208       __pthread_wait_for_restart_signal(self);
209       atomic_decrement(&self->p_resume_count); /* should be zero now! */
210     } else {
211       int was_on_queue;
212       __pthread_lock(&cond->__c_lock, self);
213       was_on_queue = remove_from_queue(&cond->__c_waiting, self);
214       __pthread_unlock(&cond->__c_lock);
215
216       if (was_on_queue) {
217         __pthread_set_own_extricate_if(self, 0);
218         pthread_mutex_lock(mutex);
219
220         if (retsleep == 0)
221           return ETIMEDOUT;
222         /* Woken by a signal: resume waiting as required by Single Unix
223            Specification.  */
224         goto requeue_and_wait_again;
225       }
226
227       suspend(self);
228     }
229   }
230
231   __pthread_set_own_extricate_if(self, 0);
232
233   /* The remaining logic is the same as in other cancellable waits,
234      such as pthread_join sem_wait or pthread_cond wait. */
235
236   if (THREAD_GETMEM(self, p_woken_by_cancel)
237       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
238     THREAD_SETMEM(self, p_woken_by_cancel, 0);
239     pthread_mutex_lock(mutex);
240     pthread_exit(PTHREAD_CANCELED);
241   }
242
243   pthread_mutex_lock(mutex);
244   return 0;
245 }
246
247 /* The following function is used on new (late 2.1 and 2.2 and higher) kernels
248    that have rt signals which queue. */
249
250 static int
251 pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
252                                 pthread_mutex_t *mutex,
253                                 struct timespec * reltime)
254 {
255   volatile pthread_descr self = thread_self();
256   sigset_t unblock, initial_mask;
257   int retsleep, already_canceled, was_signalled;
258   sigjmp_buf jmpbuf;
259   pthread_extricate_if extr;
260
261  requeue_and_wait_again:
262
263   retsleep = 0;
264   already_canceled = 0;
265   was_signalled = 0;
266
267   /* Set up extrication interface */
268   extr.pu_object = cond;
269   extr.pu_extricate_func = cond_extricate_func;
270
271   /* Register extrication interface */
272   __pthread_set_own_extricate_if(self, &extr);
273
274   /* Enqueue to wait on the condition and check for cancellation. */
275   __pthread_lock(&cond->__c_lock, self);
276   if (!(THREAD_GETMEM(self, p_canceled)
277       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
278     enqueue(&cond->__c_waiting, self);
279   else
280     already_canceled = 1;
281   __pthread_unlock(&cond->__c_lock);
282
283   if (already_canceled) {
284     __pthread_set_own_extricate_if(self, 0);
285     pthread_exit(PTHREAD_CANCELED);
286   }
287
288   pthread_mutex_unlock(mutex);
289
290   /* Set up a longjmp handler for the restart signal, unblock
291      the signal and sleep. */
292
293   if (sigsetjmp(jmpbuf, 1) == 0) {
294     THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
295     THREAD_SETMEM(self, p_signal, 0);
296     /* Unblock the restart signal */
297     sigemptyset(&unblock);
298     sigaddset(&unblock, __pthread_sig_restart);
299     sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
300     /* Sleep for the required duration */
301     retsleep = __libc_nanosleep(reltime, reltime);
302     /* Block the restart signal again */
303     sigprocmask(SIG_SETMASK, &initial_mask, NULL);
304     was_signalled = 0;
305   } else {
306     retsleep = -1;
307     was_signalled = 1;
308   }
309   THREAD_SETMEM(self, p_signal_jmp, NULL);
310
311   /* Now was_signalled is true if we exited the above code
312      due to the delivery of a restart signal.  In that case,
313      everything is cool. We have been removed from the queue
314      by the other thread, and consumed its signal.
315
316      Otherwise we this thread woke up spontaneously, or due to a signal other
317      than restart. The next thing to do is to try to remove the thread
318      from the queue. This may fail due to a race against another thread
319      trying to do the same. In the failed case, we know we were signalled,
320      and we may also have to consume a restart signal. */
321
322   if (!was_signalled) {
323     int was_on_queue;
324
325     /* __pthread_lock will queue back any spurious restarts that
326        may happen to it. */
327
328     __pthread_lock(&cond->__c_lock, self);
329     was_on_queue = remove_from_queue(&cond->__c_waiting, self);
330     __pthread_unlock(&cond->__c_lock);
331
332     if (was_on_queue) {
333       __pthread_set_own_extricate_if(self, 0);
334       pthread_mutex_lock(mutex);
335
336       if (retsleep == 0)
337         return ETIMEDOUT;
338       /* Woken by a signal: resume waiting as required by Single Unix
339          Specification.  */
340       goto requeue_and_wait_again;
341     }
342
343     /* Eat the outstanding restart() from the signaller */
344     suspend(self);
345   }
346
347   __pthread_set_own_extricate_if(self, 0);
348
349   /* The remaining logic is the same as in other cancellable waits,
350      such as pthread_join sem_wait or pthread_cond wait. */
351
352   if (THREAD_GETMEM(self, p_woken_by_cancel)
353       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
354     THREAD_SETMEM(self, p_woken_by_cancel, 0);
355     pthread_mutex_lock(mutex);
356     pthread_exit(PTHREAD_CANCELED);
357   }
358
359   pthread_mutex_lock(mutex);
360   return 0;
361 }
362
363 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
364                            const struct timespec * abstime)
365 {
366   struct timeval now;
367   struct timespec reltime;
368   /* Compute a time offset relative to now */
369   __gettimeofday(&now, NULL);
370   reltime.tv_sec = abstime->tv_sec - now.tv_sec;
371   reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
372   if (reltime.tv_nsec < 0) {
373     reltime.tv_nsec += 1000000000;
374     reltime.tv_sec -= 1;
375   }
376   if (reltime.tv_sec < 0) return ETIMEDOUT;
377
378   /* Indirect call through pointer! */
379   return pthread_cond_tw_rel(cond, mutex, &reltime);
380 }
381
382 int pthread_cond_signal(pthread_cond_t *cond)
383 {
384   pthread_descr th;
385
386   __pthread_lock(&cond->__c_lock, NULL);
387   th = dequeue(&cond->__c_waiting);
388   __pthread_unlock(&cond->__c_lock);
389   if (th != NULL) restart(th);
390   return 0;
391 }
392
393 int pthread_cond_broadcast(pthread_cond_t *cond)
394 {
395   pthread_descr tosignal, th;
396
397   __pthread_lock(&cond->__c_lock, NULL);
398   /* Copy the current state of the waiting queue and empty it */
399   tosignal = cond->__c_waiting;
400   cond->__c_waiting = NULL;
401   __pthread_unlock(&cond->__c_lock);
402   /* Now signal each process in the queue */
403   while ((th = dequeue(&tosignal)) != NULL) restart(th);
404   return 0;
405 }
406
407 int pthread_condattr_init(pthread_condattr_t *attr)
408 {
409   return 0;
410 }
411
412 int pthread_condattr_destroy(pthread_condattr_t *attr)
413 {
414   return 0;
415 }