Fix typo.
[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 int pthread_cond_init(pthread_cond_t *cond,
29                       const pthread_condattr_t *cond_attr)
30 {
31   __pthread_init_lock(&cond->__c_lock);
32   cond->__c_waiting = NULL;
33   return 0;
34 }
35
36 int pthread_cond_destroy(pthread_cond_t *cond)
37 {
38   if (cond->__c_waiting != NULL) return EBUSY;
39   return 0;
40 }
41
42 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
43 {
44   volatile pthread_descr self = thread_self();
45
46   __pthread_lock(&cond->__c_lock, self);
47   enqueue(&cond->__c_waiting, self);
48   __pthread_unlock(&cond->__c_lock);
49   pthread_mutex_unlock(mutex);
50   suspend_with_cancellation(self);
51   pthread_mutex_lock(mutex);
52   /* This is a cancellation point */
53   if (THREAD_GETMEM(self, p_canceled)
54       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
55     /* Remove ourselves from the waiting queue if we're still on it */
56     __pthread_lock(&cond->__c_lock, self);
57     remove_from_queue(&cond->__c_waiting, self);
58     __pthread_unlock(&cond->__c_lock);
59     pthread_exit(PTHREAD_CANCELED);
60   }
61   return 0;
62 }
63
64 static inline int
65 pthread_cond_timedwait_relative(pthread_cond_t *cond,
66                                 pthread_mutex_t *mutex,
67                                 const struct timespec * reltime)
68 {
69   volatile pthread_descr self = thread_self();
70   sigset_t unblock, initial_mask;
71   int retsleep;
72   sigjmp_buf jmpbuf;
73
74   /* Wait on the condition */
75   __pthread_lock(&cond->__c_lock, self);
76   enqueue(&cond->__c_waiting, self);
77   __pthread_unlock(&cond->__c_lock);
78   pthread_mutex_unlock(mutex);
79  continue_waiting:
80   /* Set up a longjmp handler for the restart and cancel signals */
81   if (sigsetjmp(jmpbuf, 1) == 0) {
82     THREAD_SETMEM(self, p_signal_jmp, &jmpbuf);
83     THREAD_SETMEM(self, p_cancel_jmp, &jmpbuf);
84     THREAD_SETMEM(self, p_signal, 0);
85     /* Check for cancellation */
86     if (THREAD_GETMEM(self, p_canceled)
87         && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
88       retsleep = -1;
89     } else {
90       /* Unblock the restart signal */
91       sigemptyset(&unblock);
92       sigaddset(&unblock, __pthread_sig_restart);
93       sigprocmask(SIG_UNBLOCK, &unblock, &initial_mask);
94       /* Sleep for the required duration */
95       retsleep = __libc_nanosleep(reltime, NULL);
96       /* Block the restart signal again */
97       sigprocmask(SIG_SETMASK, &initial_mask, NULL);
98     }
99   } else {
100     retsleep = -1;
101   }
102   THREAD_SETMEM(self, p_signal_jmp, NULL);
103   THREAD_SETMEM(self, p_cancel_jmp, NULL);
104   /* Here, either the condition was signaled (self->p_signal != 0)
105                    or we got canceled (self->p_canceled != 0)
106                    or the timeout occurred (retsleep == 0)
107                    or another interrupt occurred (retsleep == -1) */
108   /* This is a cancellation point */
109   if (THREAD_GETMEM(self, p_canceled)
110       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
111     __pthread_lock(&cond->__c_lock, self);
112     remove_from_queue(&cond->__c_waiting, self);
113     __pthread_unlock(&cond->__c_lock);
114     pthread_mutex_lock(mutex);
115     pthread_exit(PTHREAD_CANCELED);
116   }
117   /* If not signaled: also remove ourselves and return an error code, but
118      only if the timeout has elapsed.  If not, just continue waiting. */
119   if (THREAD_GETMEM(self, p_signal) == 0) {
120     if (retsleep != 0)
121       goto continue_waiting;
122     __pthread_lock(&cond->__c_lock, self);
123     remove_from_queue(&cond->__c_waiting, self);
124     __pthread_unlock(&cond->__c_lock);
125     pthread_mutex_lock(mutex);
126     return ETIMEDOUT;
127   }
128   /* Otherwise, return normally */
129   pthread_mutex_lock(mutex);
130   return 0;
131 }
132
133 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
134                            const struct timespec * abstime)
135 {
136   struct timeval now;
137   struct timespec reltime;
138   /* Compute a time offset relative to now */
139   __gettimeofday(&now, NULL);
140   reltime.tv_sec = abstime->tv_sec - now.tv_sec;
141   reltime.tv_nsec = abstime->tv_nsec - now.tv_usec * 1000;
142   if (reltime.tv_nsec < 0) {
143     reltime.tv_nsec += 1000000000;
144     reltime.tv_sec -= 1;
145   }
146   if (reltime.tv_sec < 0) return ETIMEDOUT;
147   return pthread_cond_timedwait_relative(cond, mutex, &reltime);
148 }
149
150 int pthread_cond_signal(pthread_cond_t *cond)
151 {
152   pthread_descr th;
153
154   __pthread_lock(&cond->__c_lock, NULL);
155   th = dequeue(&cond->__c_waiting);
156   __pthread_unlock(&cond->__c_lock);
157   if (th != NULL) restart(th);
158   return 0;
159 }
160
161 int pthread_cond_broadcast(pthread_cond_t *cond)
162 {
163   pthread_descr tosignal, th;
164
165   __pthread_lock(&cond->__c_lock, NULL);
166   /* Copy the current state of the waiting queue and empty it */
167   tosignal = cond->__c_waiting;
168   cond->__c_waiting = NULL;
169   __pthread_unlock(&cond->__c_lock);
170   /* Now signal each process in the queue */
171   while ((th = dequeue(&tosignal)) != NULL) restart(th);
172   return 0;
173 }
174
175 int pthread_condattr_init(pthread_condattr_t *attr)
176 {
177   return 0;
178 }
179
180 int pthread_condattr_destroy(pthread_condattr_t *attr)
181 {
182   return 0;
183 }