Rename __pthread_spin_unlock back to __pthread_unlock.
[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 /* Function called by pthread_cancel to remove the thread from
43    waiting on a condition variable queue. */
44
45 static int cond_extricate_func(void *obj, pthread_descr th)
46 {
47   volatile pthread_descr self = thread_self();
48   pthread_cond_t *cond = obj;
49   int did_remove = 0;
50
51   __pthread_lock(&cond->__c_lock, self);
52   did_remove = remove_from_queue(&cond->__c_waiting, th);
53   __pthread_unlock(&cond->__c_lock);
54
55   return did_remove;
56 }
57
58 int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
59 {
60   volatile pthread_descr self = thread_self();
61   pthread_extricate_if extr;
62   int already_canceled = 0;
63
64   /* Check whether the mutex is locked and owned by this thread.  */
65   if (mutex->__m_kind != PTHREAD_MUTEX_FAST_NP && mutex->__m_owner != self)
66     return EINVAL;
67
68   /* Set up extrication interface */
69   extr.pu_object = cond;
70   extr.pu_extricate_func = cond_extricate_func;
71
72   /* Register extrication interface */
73   __pthread_set_own_extricate_if(self, &extr);
74
75   /* Atomically enqueue thread for waiting, but only if it is not
76      canceled. If the thread is canceled, then it will fall through the
77      suspend call below, and then call pthread_exit without
78      having to worry about whether it is still on the condition variable queue.
79      This depends on pthread_cancel setting p_canceled before calling the
80      extricate function. */
81
82   __pthread_lock(&cond->__c_lock, self);
83   if (!(THREAD_GETMEM(self, p_canceled)
84       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
85     enqueue(&cond->__c_waiting, self);
86   else
87     already_canceled = 1;
88   __pthread_unlock(&cond->__c_lock);
89
90   if (already_canceled) {
91     __pthread_set_own_extricate_if(self, 0);
92     pthread_exit(PTHREAD_CANCELED);
93   }
94
95   pthread_mutex_unlock(mutex);
96
97   suspend(self);
98   __pthread_set_own_extricate_if(self, 0);
99
100   /* Check for cancellation again, to provide correct cancellation
101      point behavior */
102
103   if (THREAD_GETMEM(self, p_woken_by_cancel)
104       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
105     THREAD_SETMEM(self, p_woken_by_cancel, 0);
106     pthread_mutex_lock(mutex);
107     pthread_exit(PTHREAD_CANCELED);
108   }
109
110   pthread_mutex_lock(mutex);
111   return 0;
112 }
113
114 static int
115 pthread_cond_timedwait_relative(pthread_cond_t *cond,
116                                 pthread_mutex_t *mutex,
117                                 const struct timespec * abstime)
118 {
119   volatile pthread_descr self = thread_self();
120   int already_canceled = 0;
121   pthread_extricate_if extr;
122
123   /* Check whether the mutex is locked and owned by this thread.  */
124   if (mutex->__m_kind != PTHREAD_MUTEX_FAST_NP && mutex->__m_owner != self)
125     return EINVAL;
126
127   /* Set up extrication interface */
128   extr.pu_object = cond;
129   extr.pu_extricate_func = cond_extricate_func;
130
131   /* Register extrication interface */
132   __pthread_set_own_extricate_if(self, &extr);
133
134   /* Enqueue to wait on the condition and check for cancellation. */
135   __pthread_lock(&cond->__c_lock, self);
136   if (!(THREAD_GETMEM(self, p_canceled)
137       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
138     enqueue(&cond->__c_waiting, self);
139   else
140     already_canceled = 1;
141   __pthread_unlock(&cond->__c_lock);
142
143   if (already_canceled) {
144     __pthread_set_own_extricate_if(self, 0);
145     pthread_exit(PTHREAD_CANCELED);
146   }
147
148   pthread_mutex_unlock(mutex);
149
150   if (!timedsuspend(self, abstime)) {
151     int was_on_queue;
152
153     /* __pthread_lock will queue back any spurious restarts that
154        may happen to it. */
155
156     __pthread_lock(&cond->__c_lock, self);
157     was_on_queue = remove_from_queue(&cond->__c_waiting, self);
158     __pthread_unlock(&cond->__c_lock);
159
160     if (was_on_queue) {
161       __pthread_set_own_extricate_if(self, 0);
162       pthread_mutex_lock(mutex);
163       return ETIMEDOUT;
164     }
165
166     /* Eat the outstanding restart() from the signaller */
167     suspend(self);
168   }
169
170   __pthread_set_own_extricate_if(self, 0);
171
172   /* The remaining logic is the same as in other cancellable waits,
173      such as pthread_join sem_wait or pthread_cond wait. */
174
175   if (THREAD_GETMEM(self, p_woken_by_cancel)
176       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
177     THREAD_SETMEM(self, p_woken_by_cancel, 0);
178     pthread_mutex_lock(mutex);
179     pthread_exit(PTHREAD_CANCELED);
180   }
181
182   pthread_mutex_lock(mutex);
183   return 0;
184 }
185
186 int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
187                            const struct timespec * abstime)
188 {
189   /* Indirect call through pointer! */
190   return pthread_cond_timedwait_relative(cond, mutex, abstime);
191 }
192
193 int pthread_cond_signal(pthread_cond_t *cond)
194 {
195   pthread_descr th;
196
197   __pthread_lock(&cond->__c_lock, NULL);
198   th = dequeue(&cond->__c_waiting);
199   __pthread_unlock(&cond->__c_lock);
200   if (th != NULL) restart(th);
201   return 0;
202 }
203
204 int pthread_cond_broadcast(pthread_cond_t *cond)
205 {
206   pthread_descr tosignal, th;
207
208   __pthread_lock(&cond->__c_lock, NULL);
209   /* Copy the current state of the waiting queue and empty it */
210   tosignal = cond->__c_waiting;
211   cond->__c_waiting = NULL;
212   __pthread_unlock(&cond->__c_lock);
213   /* Now signal each process in the queue */
214   while ((th = dequeue(&tosignal)) != NULL) restart(th);
215   return 0;
216 }
217
218 int pthread_condattr_init(pthread_condattr_t *attr)
219 {
220   return 0;
221 }
222
223 int pthread_condattr_destroy(pthread_condattr_t *attr)
224 {
225   return 0;
226 }