]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libthr/thread/thr_cond.c
merge fix for boot-time hang on centos' xen
[FreeBSD/FreeBSD.git] / lib / libthr / thread / thr_cond.c
1 /*
2  * Copyright (c) 2005 David Xu <davidxu@freebsd.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <stdlib.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <limits.h>
34
35 #include "thr_private.h"
36
37 /*
38  * Prototypes
39  */
40 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
41 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
42                     const struct timespec *abstime, int cancel);
43 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
44
45 /*
46  * Double underscore versions are cancellation points.  Single underscore
47  * versions are not and are provided for libc internal usage (which
48  * shouldn't introduce cancellation points).
49  */
50 __weak_reference(__pthread_cond_wait, pthread_cond_wait);
51 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
52
53 __weak_reference(_pthread_cond_init, pthread_cond_init);
54 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
55 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
56 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
57
58 static int
59 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
60 {
61         pthread_cond_t  pcond;
62         int             rval = 0;
63
64         if ((pcond = (pthread_cond_t)
65             malloc(sizeof(struct pthread_cond))) == NULL) {
66                 rval = ENOMEM;
67         } else {
68                 /*
69                  * Initialise the condition variable structure:
70                  */
71                 _thr_umtx_init(&pcond->c_lock);
72                 pcond->c_seqno = 0;
73                 pcond->c_waiters = 0;
74                 pcond->c_wakeups = 0;
75                 if (cond_attr == NULL || *cond_attr == NULL) {
76                         pcond->c_pshared = 0;
77                         pcond->c_clockid = CLOCK_REALTIME;
78                 } else {
79                         pcond->c_pshared = (*cond_attr)->c_pshared;
80                         pcond->c_clockid = (*cond_attr)->c_clockid;
81                 }
82                 *cond = pcond;
83         }
84         /* Return the completion status: */
85         return (rval);
86 }
87
88 static int
89 init_static(struct pthread *thread, pthread_cond_t *cond)
90 {
91         int ret;
92
93         THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
94
95         if (*cond == NULL)
96                 ret = cond_init(cond, NULL);
97         else
98                 ret = 0;
99
100         THR_LOCK_RELEASE(thread, &_cond_static_lock);
101
102         return (ret);
103 }
104
105 int
106 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
107 {
108
109         *cond = NULL;
110         return (cond_init(cond, cond_attr));
111 }
112
113 int
114 _pthread_cond_destroy(pthread_cond_t *cond)
115 {
116         struct pthread_cond     *cv;
117         struct pthread          *curthread = _get_curthread();
118         int                     rval = 0;
119
120         if (*cond == NULL)
121                 rval = EINVAL;
122         else {
123                 /* Lock the condition variable structure: */
124                 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
125                 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
126                         THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
127                         return (EBUSY);
128                 }
129
130                 /*
131                  * NULL the caller's pointer now that the condition
132                  * variable has been destroyed:
133                  */
134                 cv = *cond;
135                 *cond = NULL;
136
137                 /* Unlock the condition variable structure: */
138                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
139
140                 /* Free the cond lock structure: */
141
142                 /*
143                  * Free the memory allocated for the condition
144                  * variable structure:
145                  */
146                 free(cv);
147
148         }
149         /* Return the completion status: */
150         return (rval);
151 }
152
153 struct cond_cancel_info
154 {
155         pthread_mutex_t *mutex;
156         pthread_cond_t  *cond;
157         long            seqno;
158 };
159
160 static void
161 cond_cancel_handler(void *arg)
162 {
163         struct pthread *curthread = _get_curthread();
164         struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
165         pthread_cond_t cv;
166
167         cv = *(cci->cond);
168         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
169         if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
170                 if (cv->c_waiters > 0) {
171                         cv->c_seqno++;
172                         _thr_umtx_wake(&cv->c_seqno, 1);
173                 } else
174                         cv->c_wakeups--;
175         } else {
176                 cv->c_waiters--;
177         }
178         THR_LOCK_RELEASE(curthread, &cv->c_lock);
179
180         _mutex_cv_lock(cci->mutex);
181 }
182
183 static int
184 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
185         const struct timespec *abstime, int cancel)
186 {
187         struct pthread  *curthread = _get_curthread();
188         struct timespec ts, ts2, *tsp;
189         struct cond_cancel_info cci;
190         pthread_cond_t  cv;
191         long            seq, oldseq;
192         int             oldcancel;
193         int             ret = 0;
194
195         /*
196          * If the condition variable is statically initialized,
197          * perform the dynamic initialization:
198          */
199         if (__predict_false(*cond == NULL &&
200             (ret = init_static(curthread, cond)) != 0))
201                 return (ret);
202
203         cv = *cond;
204         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
205         ret = _mutex_cv_unlock(mutex);
206         if (ret) {
207                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
208                 return (ret);
209         }
210         oldseq = seq = cv->c_seqno;
211         cci.mutex = mutex;
212         cci.cond  = cond;
213         cci.seqno = oldseq;
214
215         cv->c_waiters++;
216         do {
217                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
218
219                 if (abstime != NULL) {
220                         clock_gettime(cv->c_clockid, &ts);
221                         TIMESPEC_SUB(&ts2, abstime, &ts);
222                         tsp = &ts2;
223                 } else
224                         tsp = NULL;
225
226                 if (cancel) {
227                         THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
228                         oldcancel = _thr_cancel_enter(curthread);
229                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
230                         _thr_cancel_leave(curthread, oldcancel);
231                         THR_CLEANUP_POP(curthread, 0);
232                 } else {
233                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
234                 }
235
236                 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
237                 seq = cv->c_seqno;
238                 if (abstime != NULL && ret == ETIMEDOUT)
239                         break;
240
241                 /*
242                  * loop if we have never been told to wake up
243                  * or we lost a race.
244                  */
245         } while (seq == oldseq || cv->c_wakeups == 0);
246         
247         if (seq != oldseq && cv->c_wakeups != 0) {
248                 cv->c_wakeups--;
249                 ret = 0;
250         } else {
251                 cv->c_waiters--;
252         }
253         THR_LOCK_RELEASE(curthread, &cv->c_lock);
254         _mutex_cv_lock(mutex);
255         return (ret);
256 }
257
258 int
259 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
260 {
261
262         return (cond_wait_common(cond, mutex, NULL, 0));
263 }
264
265 int
266 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
267 {
268
269         return (cond_wait_common(cond, mutex, NULL, 1));
270 }
271
272 int
273 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
274                        const struct timespec * abstime)
275 {
276
277         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
278             abstime->tv_nsec >= 1000000000)
279                 return (EINVAL);
280
281         return (cond_wait_common(cond, mutex, abstime, 0));
282 }
283
284 int
285 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
286                        const struct timespec *abstime)
287 {
288
289         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
290             abstime->tv_nsec >= 1000000000)
291                 return (EINVAL);
292
293         return (cond_wait_common(cond, mutex, abstime, 1));
294 }
295
296 static int
297 cond_signal_common(pthread_cond_t *cond, int broadcast)
298 {
299         struct pthread  *curthread = _get_curthread();
300         pthread_cond_t  cv;
301         int             ret = 0, oldwaiters;
302
303         /*
304          * If the condition variable is statically initialized, perform dynamic
305          * initialization.
306          */
307         if (__predict_false(*cond == NULL &&
308             (ret = init_static(curthread, cond)) != 0))
309                 return (ret);
310
311         cv = *cond;
312         /* Lock the condition variable structure. */
313         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
314         if (cv->c_waiters) {
315                 if (!broadcast) {
316                         cv->c_wakeups++;
317                         cv->c_waiters--;
318                         cv->c_seqno++;
319                         _thr_umtx_wake(&cv->c_seqno, 1);
320                 } else {
321                         oldwaiters = cv->c_waiters;
322                         cv->c_wakeups += cv->c_waiters;
323                         cv->c_waiters = 0;
324                         cv->c_seqno++;
325                         _thr_umtx_wake(&cv->c_seqno, oldwaiters);
326                 }
327         }
328         THR_LOCK_RELEASE(curthread, &cv->c_lock);
329         return (ret);
330 }
331
332 int
333 _pthread_cond_signal(pthread_cond_t * cond)
334 {
335
336         return (cond_signal_common(cond, 0));
337 }
338
339 int
340 _pthread_cond_broadcast(pthread_cond_t * cond)
341 {
342
343         return (cond_signal_common(cond, 1));
344 }