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