(pthread_cond_timedwait_relative_old): Undo last patch but keep the code
[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 #ifdef NANOSLEEP_WORKS
138   int already_canceled = 0;
139   int was_signalled = 0;
140 #else
141   int retsleep;
142   int already_canceled;
143   int was_signalled;
144 #endif
145   sigjmp_buf jmpbuf;
146   pthread_extricate_if extr;
147
148 #ifndef NANOSLEEP_WORKS
149  requeue_and_wait_again:
150
151   retsleep = 0;
152   already_canceled = 0;
153   was_signalled = 0;
154 #endif
155
156   /* Set up extrication interface */
157   extr.pu_object = cond;
158   extr.pu_extricate_func = cond_extricate_func;
159
160   /* Register extrication interface */
161   __pthread_set_own_extricate_if(self, &extr);
162
163   /* Enqueue to wait on the condition and check for cancellation. */
164   __pthread_lock(&cond->__c_lock, self);
165   if (!(THREAD_GETMEM(self, p_canceled)
166       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
167     enqueue(&cond->__c_waiting, self);
168   else
169     already_canceled = 1;
170   __pthread_unlock(&cond->__c_lock);
171
172   if (already_canceled) {
173     __pthread_set_own_extricate_if(self, 0);
174     pthread_exit(PTHREAD_CANCELED);
175   }
176
177   pthread_mutex_unlock(mutex);
178
179   if (atomic_decrement(&self->p_resume_count) == 0) {
180     /* Set up a longjmp handler for the restart signal, unblock
181        the signal and sleep. */
182
183     if (sigsetjmp(jmpbuf, 1) == 0) {
184       THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
185       THREAD_SETMEM(self, p_signal, 0);
186       /* Unblock the restart signal */
187       sigemptyset(&unblock);
188       sigaddset(&unblock, __pthread_sig_restart);
189       sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
190 #ifdef NANOSLEEP_WORKS
191       /* Sleep for the required duration. If woken by a signal, resume waiting
192          as required by Single Unix Specification.  */
193       while (__libc_nanosleep(reltime, reltime) != 0)
194         ;
195 #else
196       /* Sleep for the required duration */
197       retsleep = __libc_nanosleep(&reltime, NULL);
198 #endif
199       /* Block the restart signal again */
200       sigprocmask(SIG_SETMASK, &initial_mask, NULL);
201       was_signalled = 0;
202     } else {
203 #ifndef NANOSLEEP_WORKS
204       retsleep = -1;
205 #endif
206       was_signalled = 1;
207     }
208     THREAD_SETMEM(self, p_signal_jmp, NULL);
209   }
210
211   /* Now was_signalled is true if we exited the above code
212      due to the delivery of a restart signal.  In that case,
213      we know we have been dequeued and resumed and that the
214      resume count is balanced.  Otherwise, there are some
215      cases to consider. First, try to bump up the resume count
216      back to zero. If it goes to 1, it means restart() was
217      invoked on this thread. The signal must be consumed
218      and the count bumped down and everything is cool.
219      Otherwise, no restart was delivered yet, so we remove
220      the thread from the queue. If this succeeds, it's a clear
221      case of timeout. If we fail to remove from the queue, then we
222      must wait for a restart. */
223
224   if (!was_signalled) {
225     if (atomic_increment(&self->p_resume_count) != -1) {
226       __pthread_wait_for_restart_signal(self);
227       atomic_decrement(&self->p_resume_count); /* should be zero now! */
228     } else {
229       int was_on_queue;
230       __pthread_lock(&cond->__c_lock, self);
231       was_on_queue = remove_from_queue(&cond->__c_waiting, self);
232       __pthread_unlock(&cond->__c_lock);
233
234       if (was_on_queue) {
235         __pthread_set_own_extricate_if(self, 0);
236         pthread_mutex_lock(mutex);
237 #ifdef NANOSLEEP_WORKS
238         return ETIMEDOUT;
239 #else
240         if (retsleep == 0)
241           return ETIMEDOUT;
242         /* Woken by a signal: resume waiting as required by Single Unix
243            Specification.  */
244         goto requeue_and_wait_again;
245 #endif
246       }
247
248       suspend(self);
249     }
250   }
251
252   __pthread_set_own_extricate_if(self, 0);
253
254   /* The remaining logic is the same as in other cancellable waits,
255      such as pthread_join sem_wait or pthread_cond wait. */
256
257   if (THREAD_GETMEM(self, p_woken_by_cancel)
258       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
259     THREAD_SETMEM(self, p_woken_by_cancel, 0);
260     pthread_mutex_lock(mutex);
261     pthread_exit(PTHREAD_CANCELED);
262   }
263
264   pthread_mutex_lock(mutex);
265   return 0;
266 }
267
268 /* The following function is used on new (late 2.1 and 2.2 and higher) kernels
269    that have rt signals which queue. */
270
271 static int
272 pthread_cond_timedwait_relative_new(pthread_cond_t *cond,
273                                 pthread_mutex_t *mutex,
274                                 struct timespec * reltime)
275 {
276   volatile pthread_descr self = thread_self();
277   sigset_t unblock, initial_mask;
278 #ifdef NANOSLEEP_WORKS
279   int already_canceled = 0;
280   int was_signalled = 0;
281 #else
282   int retsleep;
283   int already_canceled;
284   int was_signalled;
285 #endif
286   sigjmp_buf jmpbuf;
287   pthread_extricate_if extr;
288
289   already_canceled = 0;
290   was_signalled = 0;
291
292 #ifndef NANOSLEEP_WORKS
293  requeue_and_wait_again:
294
295   retsleep = 0;
296   already_canceled = 0;
297   was_signalled = 0;
298 #endif
299
300   /* Set up extrication interface */
301   extr.pu_object = cond;
302   extr.pu_extricate_func = cond_extricate_func;
303
304   /* Register extrication interface */
305   __pthread_set_own_extricate_if(self, &extr);
306
307   /* Enqueue to wait on the condition and check for cancellation. */
308   __pthread_lock(&cond->__c_lock, self);
309   if (!(THREAD_GETMEM(self, p_canceled)
310       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
311     enqueue(&cond->__c_waiting, self);
312   else
313     already_canceled = 1;
314   __pthread_unlock(&cond->__c_lock);
315
316   if (already_canceled) {
317     __pthread_set_own_extricate_if(self, 0);
318     pthread_exit(PTHREAD_CANCELED);
319   }
320
321   pthread_mutex_unlock(mutex);
322
323   /* Set up a longjmp handler for the restart signal, unblock
324      the signal and sleep. */
325
326   if (sigsetjmp(jmpbuf, 1) == 0) {
327     THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
328     THREAD_SETMEM(self, p_signal, 0);
329     /* Unblock the restart signal */
330     sigemptyset(&unblock);
331     sigaddset(&unblock, __pthread_sig_restart);
332     sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
333 #ifdef NANOSLEEP_WORKS
334     /* Sleep for the required duration. If woken by a signal, resume waiting
335        as required by Single Unix Specification.  */
336     while (__libc_nanosleep(reltime, reltime) != 0)
337       ;
338 #else
339     /* Sleep for the required duration */
340     retsleep = __libc_nanosleep(&reltime, NULL);
341 #endif
342     /* Block the restart signal again */
343     sigprocmask(SIG_SETMASK, &initial_mask, NULL);
344     was_signalled = 0;
345   } else {
346 #ifndef NANOSLEEP_WORKS
347     retsleep = -1;
348 #endif
349     was_signalled = 1;
350   }
351   THREAD_SETMEM(self, p_signal_jmp, NULL);
352
353   /* Now was_signalled is true if we exited the above code
354      due to the delivery of a restart signal.  In that case,
355      everything is cool. We have been removed from the queue
356      by the other thread, and consumed its signal.
357
358      Otherwise we this thread woke up spontaneously, or due to a signal other
359      than restart. The next thing to do is to try to remove the thread
360      from the queue. This may fail due to a race against another thread
361      trying to do the same. In the failed case, we know we were signalled,
362      and we may also have to consume a restart signal. */
363
364   if (!was_signalled) {
365     int was_on_queue;
366
367     /* __pthread_lock will queue back any spurious restarts that
368        may happen to it. */
369
370     __pthread_lock(&cond->__c_lock, self);
371     was_on_queue = remove_from_queue(&cond->__c_waiting, self);
372     __pthread_unlock(&cond->__c_lock);
373
374     if (was_on_queue) {
375       __pthread_set_own_extricate_if(self, 0);
376       pthread_mutex_lock(mutex);
377 #ifdef NANOSLEEP_WORKS
378       return ETIMEDOUT;
379 #else
380       if (retsleep == 0)
381         return ETIMEDOUT;
382       /* Woken by a signal: resume waiting as required by Single Unix
383          Specification.  */
384       goto requeue_and_wait_again;
385 #endif
386     }
387
388     /* Eat the outstanding restart() from the signaller */
389     suspend(self);
390   }
391
392   __pthread_set_own_extricate_if(self, 0);
393
394   /* The remaining logic is the same as in other cancellable waits,
395      such as pthread_join sem_wait or pthread_cond wait. */
396
397   if (THREAD_GETMEM(self, p_woken_by_cancel)
398       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
399     THREAD_SETMEM(self, p_woken_by_cancel, 0);
400     pthread_mutex_lock(mutex);
401     pthread_exit(PTHREAD_CANCELED);
402   }
403
404   pthread_mutex_lock(mutex);
405   return 0;
406 }
407
408 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
409                            const struct timespec * abstime)
410 {
411   struct timeval now;
412   struct timespec reltime;
413
414   /* Compute a time offset relative to now.  */
415   __gettimeofday (&now, NULL);
416   reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
417   reltime.tv_sec = abstime->tv_sec - now.tv_sec;
418   if (reltime.tv_nsec < 0) {
419     reltime.tv_nsec += 1000000000;
420     reltime.tv_sec -= 1;
421   }
422   if (reltime.tv_sec < 0)
423     return ETIMEDOUT;
424
425   /* Indirect call through pointer! */
426   return pthread_cond_tw_rel(cond, mutex, &reltime);
427 }
428
429 int pthread_cond_signal(pthread_cond_t *cond)
430 {
431   pthread_descr th;
432
433   __pthread_lock(&cond->__c_lock, NULL);
434   th = dequeue(&cond->__c_waiting);
435   __pthread_unlock(&cond->__c_lock);
436   if (th != NULL) restart(th);
437   return 0;
438 }
439
440 int pthread_cond_broadcast(pthread_cond_t *cond)
441 {
442   pthread_descr tosignal, th;
443
444   __pthread_lock(&cond->__c_lock, NULL);
445   /* Copy the current state of the waiting queue and empty it */
446   tosignal = cond->__c_waiting;
447   cond->__c_waiting = NULL;
448   __pthread_unlock(&cond->__c_lock);
449   /* Now signal each process in the queue */
450   while ((th = dequeue(&tosignal)) != NULL) restart(th);
451   return 0;
452 }
453
454 int pthread_condattr_init(pthread_condattr_t *attr)
455 {
456   return 0;
457 }
458
459 int pthread_condattr_destroy(pthread_condattr_t *attr)
460 {
461   return 0;
462 }