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