Merge from vendor branch NTPD:
[dragonfly.git] / lib / libthread_xu / 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  * $DragonFly: src/lib/libthread_xu/thread/thr_cond.c,v 1.1 2005/02/01 12:38:27 davidxu Exp $
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         *cond = NULL;
109         return cond_init(cond, cond_attr);
110 }
111
112 int
113 _pthread_cond_destroy(pthread_cond_t *cond)
114 {
115         struct pthread_cond     *cv;
116         struct pthread          *curthread = _get_curthread();
117         int                     rval = 0;
118
119         if (*cond == NULL)
120                 rval = EINVAL;
121         else {
122                 /* Lock the condition variable structure: */
123                 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
124                 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
125                         THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
126                         return (EBUSY);
127                 }
128
129                 /*
130                  * NULL the caller's pointer now that the condition
131                  * variable has been destroyed:
132                  */
133                 cv = *cond;
134                 *cond = NULL;
135
136                 /* Unlock the condition variable structure: */
137                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
138
139                 /* Free the cond lock structure: */
140
141                 /*
142                  * Free the memory allocated for the condition
143                  * variable structure:
144                  */
145                 free(cv);
146
147         }
148         /* Return the completion status: */
149         return (rval);
150 }
151
152 struct cond_cancel_info
153 {
154         pthread_mutex_t *mutex;
155         pthread_cond_t  *cond;
156         long            seqno;
157 };
158
159 static void
160 cond_cancel_handler(void *arg)
161 {
162         struct pthread *curthread = _get_curthread();
163         struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
164         pthread_cond_t cv;
165
166         cv = *(cci->cond);
167         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
168         if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
169                 if (cv->c_waiters > 0) {
170                         cv->c_seqno++;
171                         _thr_umtx_wake(&cv->c_seqno, 1);
172                 } else
173                         cv->c_wakeups--;
174         } else {
175                 cv->c_waiters--;
176         }
177         THR_LOCK_RELEASE(curthread, &cv->c_lock);
178
179         _mutex_cv_lock(cci->mutex);
180 }
181
182 static int
183 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
184         const struct timespec *abstime, int cancel)
185 {
186         struct pthread  *curthread = _get_curthread();
187         struct timespec ts, ts2, *tsp;
188         struct cond_cancel_info cci;
189         pthread_cond_t  cv;
190         long            seq, oldseq;
191         int             oldcancel;
192         int             ret = 0;
193
194         /*
195          * If the condition variable is statically initialized,
196          * perform the dynamic initialization:
197          */
198         if (__predict_false(*cond == NULL &&
199             (ret = init_static(curthread, cond)) != 0))
200                 return (ret);
201
202         cv = *cond;
203         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
204         ret = _mutex_cv_unlock(mutex);
205         if (ret) {
206                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
207                 return (ret);
208         }
209         oldseq = seq = cv->c_seqno;
210         cci.mutex = mutex;
211         cci.cond  = cond;
212         cci.seqno = oldseq;
213
214         cv->c_waiters++;
215         do {
216                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
217
218                 if (abstime != NULL) {
219                         clock_gettime(cv->c_clockid, &ts);
220                         TIMESPEC_SUB(&ts2, abstime, &ts);
221                         tsp = &ts2;
222                 } else
223                         tsp = NULL;
224
225                 if (cancel) {
226                         THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
227                         oldcancel = _thr_cancel_enter(curthread);
228                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
229                         _thr_cancel_leave(curthread, oldcancel);
230                         THR_CLEANUP_POP(curthread, 0);
231                 } else {
232                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp);
233                 }
234
235                 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
236                 seq = cv->c_seqno;
237                 if (abstime != NULL && ret == ETIMEDOUT)
238                         break;
239
240                 /*
241                  * loop if we have never been told to wake up
242                  * or we lost a race.
243                  */
244         } while (seq == oldseq || cv->c_wakeups == 0);
245         
246         if (seq != oldseq && cv->c_wakeups != 0) {
247                 cv->c_wakeups--;
248                 ret = 0;
249         } else {
250                 cv->c_waiters--;
251         }
252         THR_LOCK_RELEASE(curthread, &cv->c_lock);
253         _mutex_cv_lock(mutex);
254         return (ret);
255 }
256
257 int
258 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
259 {
260         return cond_wait_common(cond, mutex, NULL, 0);
261 }
262
263 __strong_reference(_pthread_cond_wait, _thr_cond_wait);
264
265 int
266 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
267 {
268         int ret;
269
270         ret = cond_wait_common(cond, mutex, NULL, 1);
271         return (ret);
272 }
273
274 int
275 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
276                        const struct timespec * abstime)
277 {
278         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
279             abstime->tv_nsec >= 1000000000)
280                 return (EINVAL);
281
282         return cond_wait_common(cond, mutex, abstime, 0);
283 }
284
285 __strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
286
287 int
288 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
289                        const struct timespec *abstime)
290 {
291         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
292             abstime->tv_nsec >= 1000000000)
293                 return (EINVAL);
294
295         return cond_wait_common(cond, mutex, abstime, 1);
296 }
297
298 static int
299 cond_signal_common(pthread_cond_t *cond, int broadcast)
300 {
301         struct pthread  *curthread = _get_curthread();
302         pthread_cond_t  cv;
303         int             ret = 0;
304
305         /*
306          * If the condition variable is statically initialized, perform dynamic
307          * initialization.
308          */
309         if (__predict_false(*cond == NULL &&
310             (ret = init_static(curthread, cond)) != 0))
311                 return (ret);
312
313         cv = *cond;
314         /* Lock the condition variable structure. */
315         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
316         if (cv->c_waiters) {
317                 if (!broadcast) {
318                         cv->c_wakeups++;
319                         cv->c_waiters--;
320                         cv->c_seqno++;
321                         _thr_umtx_wake(&cv->c_seqno, 1);
322                 } else {
323                         cv->c_wakeups += cv->c_waiters;
324                         cv->c_waiters = 0;
325                         cv->c_seqno++;
326                         _thr_umtx_wake(&cv->c_seqno, INT_MAX);
327                 }
328         }
329         THR_LOCK_RELEASE(curthread, &cv->c_lock);
330         return (ret);
331 }
332
333 int
334 _pthread_cond_signal(pthread_cond_t * cond)
335 {
336         return cond_signal_common(cond, 0);
337 }
338
339 __strong_reference(_pthread_cond_signal, _thr_cond_signal);
340
341 int
342 _pthread_cond_broadcast(pthread_cond_t * cond)
343 {
344         return cond_signal_common(cond, 1);
345 }
346
347 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);