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