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