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 <stddef.h>
20 #include <sys/time.h>
21 #include "pthread.h"
22 #include "internals.h"
23 #include "spinlock.h"
24 #include "queue.h"
25 #include "restart.h"
26 
27 int
28 attribute_hidden
__pthread_cond_init(pthread_cond_t * cond,const pthread_condattr_t * cond_attr)29 __pthread_cond_init(pthread_cond_t *cond,
30                         const pthread_condattr_t *cond_attr)
31 {
32   __pthread_init_lock(&cond->__c_lock);
33   cond->__c_waiting = NULL;
34   return 0;
35 }
strong_alias(__pthread_cond_init,pthread_cond_init)36 strong_alias (__pthread_cond_init, pthread_cond_init)
37 
38 int
39 attribute_hidden
40 __pthread_cond_destroy(pthread_cond_t *cond)
41 {
42   if (cond->__c_waiting != NULL) return EBUSY;
43   return 0;
44 }
strong_alias(__pthread_cond_destroy,pthread_cond_destroy)45 strong_alias (__pthread_cond_destroy, pthread_cond_destroy)
46 
47 /* Function called by pthread_cancel to remove the thread from
48    waiting on a condition variable queue. */
49 
50 static int cond_extricate_func(void *obj, pthread_descr th)
51 {
52   __volatile__ pthread_descr self = thread_self();
53   pthread_cond_t *cond = obj;
54   int did_remove = 0;
55 
56   __pthread_lock(&cond->__c_lock, self);
57   did_remove = remove_from_queue(&cond->__c_waiting, th);
58   __pthread_unlock(&cond->__c_lock);
59 
60   return did_remove;
61 }
62 
63 int
64 attribute_hidden
__pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)65 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
66 {
67   __volatile__ pthread_descr self = thread_self();
68   pthread_extricate_if extr;
69   int already_canceled = 0;
70   int spurious_wakeup_count;
71 
72   /* Check whether the mutex is locked and owned by this thread.  */
73   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
74       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
75       && mutex->__m_owner != self)
76     return EINVAL;
77 
78   /* Set up extrication interface */
79   extr.pu_object = cond;
80   extr.pu_extricate_func = cond_extricate_func;
81 
82   /* Register extrication interface */
83   THREAD_SETMEM(self, p_condvar_avail, 0);
84   __pthread_set_own_extricate_if(self, &extr);
85 
86   /* Atomically enqueue thread for waiting, but only if it is not
87      canceled. If the thread is canceled, then it will fall through the
88      suspend call below, and then call pthread_exit without
89      having to worry about whether it is still on the condition variable queue.
90      This depends on pthread_cancel setting p_canceled before calling the
91      extricate function. */
92 
93   __pthread_lock(&cond->__c_lock, self);
94   if (!(THREAD_GETMEM(self, p_canceled)
95       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
96     enqueue(&cond->__c_waiting, self);
97   else
98     already_canceled = 1;
99   __pthread_unlock(&cond->__c_lock);
100 
101   if (already_canceled) {
102     __pthread_set_own_extricate_if(self, 0);
103     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
104   }
105 
106   pthread_mutex_unlock(mutex);
107 
108   spurious_wakeup_count = 0;
109   while (1)
110     {
111       suspend(self);
112       if (THREAD_GETMEM(self, p_condvar_avail) == 0
113 	  && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
114 	      || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
115 	{
116 	  /* Count resumes that don't belong to us. */
117 	  spurious_wakeup_count++;
118 	  continue;
119 	}
120       break;
121     }
122 
123   __pthread_set_own_extricate_if(self, 0);
124 
125   /* Check for cancellation again, to provide correct cancellation
126      point behavior */
127 
128   if (THREAD_GETMEM(self, p_woken_by_cancel)
129       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
130     THREAD_SETMEM(self, p_woken_by_cancel, 0);
131     pthread_mutex_lock(mutex);
132     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
133   }
134 
135   /* Put back any resumes we caught that don't belong to us. */
136   while (spurious_wakeup_count--)
137     restart(self);
138 
139   pthread_mutex_lock(mutex);
140   return 0;
141 }
strong_alias(__pthread_cond_wait,pthread_cond_wait)142 strong_alias (__pthread_cond_wait, pthread_cond_wait)
143 
144 static int
145 pthread_cond_timedwait_relative(pthread_cond_t *cond,
146 				pthread_mutex_t *mutex,
147 				const struct timespec * abstime)
148 {
149   __volatile__ pthread_descr self = thread_self();
150   int already_canceled = 0;
151   pthread_extricate_if extr;
152   int spurious_wakeup_count;
153 
154   /* Check whether the mutex is locked and owned by this thread.  */
155   if (mutex->__m_kind != PTHREAD_MUTEX_TIMED_NP
156       && mutex->__m_kind != PTHREAD_MUTEX_ADAPTIVE_NP
157       && mutex->__m_owner != self)
158     return EINVAL;
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   THREAD_SETMEM(self, p_condvar_avail, 0);
166   __pthread_set_own_extricate_if(self, &extr);
167 
168   /* Enqueue to wait on the condition and check for cancellation. */
169   __pthread_lock(&cond->__c_lock, self);
170   if (!(THREAD_GETMEM(self, p_canceled)
171       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE))
172     enqueue(&cond->__c_waiting, self);
173   else
174     already_canceled = 1;
175   __pthread_unlock(&cond->__c_lock);
176 
177   if (already_canceled) {
178     __pthread_set_own_extricate_if(self, 0);
179     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
180   }
181 
182   pthread_mutex_unlock(mutex);
183 
184   spurious_wakeup_count = 0;
185   while (1)
186     {
187       if (!timedsuspend(self, abstime)) {
188 	int was_on_queue;
189 
190 	/* __pthread_lock will queue back any spurious restarts that
191 	   may happen to it. */
192 
193 	__pthread_lock(&cond->__c_lock, self);
194 	was_on_queue = remove_from_queue(&cond->__c_waiting, self);
195 	__pthread_unlock(&cond->__c_lock);
196 
197 	if (was_on_queue) {
198 	  __pthread_set_own_extricate_if(self, 0);
199 	  pthread_mutex_lock(mutex);
200 	  return ETIMEDOUT;
201 	}
202 
203 	/* Eat the outstanding restart() from the signaller */
204 	suspend(self);
205       }
206 
207       if (THREAD_GETMEM(self, p_condvar_avail) == 0
208 	  && (THREAD_GETMEM(self, p_woken_by_cancel) == 0
209 	      || THREAD_GETMEM(self, p_cancelstate) != PTHREAD_CANCEL_ENABLE))
210 	{
211 	  /* Count resumes that don't belong to us. */
212 	  spurious_wakeup_count++;
213 	  continue;
214 	}
215       break;
216     }
217 
218   __pthread_set_own_extricate_if(self, 0);
219 
220   /* The remaining logic is the same as in other cancellable waits,
221      such as pthread_join sem_wait or pthread_cond wait. */
222 
223   if (THREAD_GETMEM(self, p_woken_by_cancel)
224       && THREAD_GETMEM(self, p_cancelstate) == PTHREAD_CANCEL_ENABLE) {
225     THREAD_SETMEM(self, p_woken_by_cancel, 0);
226     pthread_mutex_lock(mutex);
227     __pthread_do_exit(PTHREAD_CANCELED, CURRENT_STACK_FRAME);
228   }
229 
230   /* Put back any resumes we caught that don't belong to us. */
231   while (spurious_wakeup_count--)
232     restart(self);
233 
234   pthread_mutex_lock(mutex);
235   return 0;
236 }
237 
238 int
239 attribute_hidden
__pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)240 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
241 			     const struct timespec * abstime)
242 {
243   /* Indirect call through pointer! */
244   return pthread_cond_timedwait_relative(cond, mutex, abstime);
245 }
strong_alias(__pthread_cond_timedwait,pthread_cond_timedwait)246 strong_alias (__pthread_cond_timedwait, pthread_cond_timedwait)
247 
248 int
249 attribute_hidden
250 __pthread_cond_signal(pthread_cond_t *cond)
251 {
252   pthread_descr th;
253 
254   __pthread_lock(&cond->__c_lock, NULL);
255   th = dequeue(&cond->__c_waiting);
256   __pthread_unlock(&cond->__c_lock);
257   if (th != NULL) {
258     th->p_condvar_avail = 1;
259     WRITE_MEMORY_BARRIER();
260     restart(th);
261   }
262   return 0;
263 }
strong_alias(__pthread_cond_signal,pthread_cond_signal)264 strong_alias (__pthread_cond_signal, pthread_cond_signal)
265 
266 int
267 attribute_hidden
268 __pthread_cond_broadcast(pthread_cond_t *cond)
269 {
270   pthread_descr tosignal, th;
271 
272   __pthread_lock(&cond->__c_lock, NULL);
273   /* Copy the current state of the waiting queue and empty it */
274   tosignal = cond->__c_waiting;
275   cond->__c_waiting = NULL;
276   __pthread_unlock(&cond->__c_lock);
277   /* Now signal each process in the queue */
278   while ((th = dequeue(&tosignal)) != NULL) {
279     th->p_condvar_avail = 1;
280     WRITE_MEMORY_BARRIER();
281     restart(th);
282   }
283   return 0;
284 }
strong_alias(__pthread_cond_broadcast,pthread_cond_broadcast)285 strong_alias (__pthread_cond_broadcast, pthread_cond_broadcast)
286 
287 int
288 attribute_hidden
289 __pthread_condattr_init(pthread_condattr_t *attr)
290 {
291   return 0;
292 }
strong_alias(__pthread_condattr_init,pthread_condattr_init)293 strong_alias (__pthread_condattr_init, pthread_condattr_init)
294 
295 int
296 attribute_hidden
297 __pthread_condattr_destroy(pthread_condattr_t *attr)
298 {
299   return 0;
300 }
strong_alias(__pthread_condattr_destroy,pthread_condattr_destroy)301 strong_alias (__pthread_condattr_destroy, pthread_condattr_destroy)
302 
303 int pthread_condattr_getpshared (const pthread_condattr_t *attr, int *pshared)
304 {
305   *pshared = PTHREAD_PROCESS_PRIVATE;
306   return 0;
307 }
308 
pthread_condattr_setpshared(pthread_condattr_t * attr,int pshared)309 int pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared)
310 {
311   if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_SHARED)
312     return EINVAL;
313 
314   /* For now it is not possible to shared a conditional variable.  */
315   if (pshared != PTHREAD_PROCESS_PRIVATE)
316     return ENOSYS;
317 
318   return 0;
319 }
320