Cleanup the TLS implementation:
[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.4 2005/03/29 19:26:20 joerg Exp $
27  */
28
29 #include <machine/tls.h>
30
31 #include <stdlib.h>
32 #include <errno.h>
33 #include <string.h>
34 #include <pthread.h>
35 #include <limits.h>
36
37 #include "thr_private.h"
38
39 /*
40  * Prototypes
41  */
42 static int cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr);
43 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
44                     const struct timespec *abstime, int cancel);
45 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
46
47 /*
48  * Double underscore versions are cancellation points.  Single underscore
49  * versions are not and are provided for libc internal usage (which
50  * shouldn't introduce cancellation points).
51  */
52 __weak_reference(__pthread_cond_wait, pthread_cond_wait);
53 __weak_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
54
55 __weak_reference(_pthread_cond_init, pthread_cond_init);
56 __weak_reference(_pthread_cond_destroy, pthread_cond_destroy);
57 __weak_reference(_pthread_cond_signal, pthread_cond_signal);
58 __weak_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
59
60 static int
61 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
62 {
63         pthread_cond_t  pcond;
64         int             rval = 0;
65
66         if ((pcond = (pthread_cond_t)
67             malloc(sizeof(struct pthread_cond))) == NULL) {
68                 rval = ENOMEM;
69         } else {
70                 /*
71                  * Initialise the condition variable structure:
72                  */
73                 _thr_umtx_init(&pcond->c_lock);
74                 pcond->c_seqno = 0;
75                 pcond->c_waiters = 0;
76                 pcond->c_wakeups = 0;
77                 if (cond_attr == NULL || *cond_attr == NULL) {
78                         pcond->c_pshared = 0;
79                         pcond->c_clockid = CLOCK_REALTIME;
80                 } else {
81                         pcond->c_pshared = (*cond_attr)->c_pshared;
82                         pcond->c_clockid = (*cond_attr)->c_clockid;
83                 }
84                 *cond = pcond;
85         }
86         /* Return the completion status: */
87         return (rval);
88 }
89
90 static int
91 init_static(struct pthread *thread, pthread_cond_t *cond)
92 {
93         int ret;
94
95         THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
96
97         if (*cond == NULL)
98                 ret = cond_init(cond, NULL);
99         else
100                 ret = 0;
101
102         THR_LOCK_RELEASE(thread, &_cond_static_lock);
103
104         return (ret);
105 }
106
107 int
108 _pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
109 {
110         *cond = NULL;
111         return cond_init(cond, cond_attr);
112 }
113
114 int
115 _pthread_cond_destroy(pthread_cond_t *cond)
116 {
117         struct pthread_cond     *cv;
118         struct pthread          *curthread = tls_get_curthread();
119         int                     rval = 0;
120
121         if (*cond == NULL)
122                 rval = EINVAL;
123         else {
124                 /* Lock the condition variable structure: */
125                 THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
126                 if ((*cond)->c_waiters + (*cond)->c_wakeups != 0) {
127                         THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
128                         return (EBUSY);
129                 }
130
131                 /*
132                  * NULL the caller's pointer now that the condition
133                  * variable has been destroyed:
134                  */
135                 cv = *cond;
136                 *cond = NULL;
137
138                 /* Unlock the condition variable structure: */
139                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
140
141                 /* Free the cond lock structure: */
142
143                 /*
144                  * Free the memory allocated for the condition
145                  * variable structure:
146                  */
147                 free(cv);
148
149         }
150         /* Return the completion status: */
151         return (rval);
152 }
153
154 struct cond_cancel_info
155 {
156         pthread_mutex_t *mutex;
157         pthread_cond_t  *cond;
158         long            seqno;
159 };
160
161 static void
162 cond_cancel_handler(void *arg)
163 {
164         struct pthread *curthread = tls_get_curthread();
165         struct cond_cancel_info *cci = (struct cond_cancel_info *)arg;
166         pthread_cond_t cv;
167
168         cv = *(cci->cond);
169         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
170         if (cv->c_seqno != cci->seqno && cv->c_wakeups != 0) {
171                 if (cv->c_waiters > 0) {
172                         cv->c_seqno++;
173                         _thr_umtx_wake(&cv->c_seqno, 1);
174                 } else
175                         cv->c_wakeups--;
176         } else {
177                 cv->c_waiters--;
178         }
179         THR_LOCK_RELEASE(curthread, &cv->c_lock);
180
181         _mutex_cv_lock(cci->mutex);
182 }
183
184 static int
185 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
186         const struct timespec *abstime, int cancel)
187 {
188         struct pthread  *curthread = tls_get_curthread();
189         struct timespec ts, ts2, *tsp;
190         struct cond_cancel_info cci;
191         pthread_cond_t  cv;
192         long            seq, oldseq;
193         int             oldcancel;
194         int             ret = 0;
195
196         /*
197          * If the condition variable is statically initialized,
198          * perform the dynamic initialization:
199          */
200         if (__predict_false(*cond == NULL &&
201             (ret = init_static(curthread, cond)) != 0))
202                 return (ret);
203
204         cv = *cond;
205         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
206         ret = _mutex_cv_unlock(mutex);
207         if (ret) {
208                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
209                 return (ret);
210         }
211         oldseq = seq = cv->c_seqno;
212         cci.mutex = mutex;
213         cci.cond  = cond;
214         cci.seqno = oldseq;
215
216         cv->c_waiters++;
217         do {
218                 THR_LOCK_RELEASE(curthread, &cv->c_lock);
219
220                 if (abstime != NULL) {
221                         clock_gettime(cv->c_clockid, &ts);
222                         TIMESPEC_SUB(&ts2, abstime, &ts);
223                         tsp = &ts2;
224                 } else
225                         tsp = NULL;
226
227                 if (cancel) {
228                         THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &cci);
229                         oldcancel = _thr_cancel_enter(curthread);
230                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
231                                 cv->c_clockid);
232                         _thr_cancel_leave(curthread, oldcancel);
233                         THR_CLEANUP_POP(curthread, 0);
234                 } else {
235                         ret = _thr_umtx_wait(&cv->c_seqno, seq, tsp,
236                                 cv->c_clockid);
237                 }
238
239                 THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
240                 seq = cv->c_seqno;
241                 if (abstime != NULL && ret == ETIMEDOUT)
242                         break;
243
244                 /*
245                  * loop if we have never been told to wake up
246                  * or we lost a race.
247                  */
248         } while (seq == oldseq || cv->c_wakeups == 0);
249         
250         if (seq != oldseq && cv->c_wakeups != 0) {
251                 cv->c_wakeups--;
252                 ret = 0;
253         } else {
254                 cv->c_waiters--;
255         }
256         THR_LOCK_RELEASE(curthread, &cv->c_lock);
257         _mutex_cv_lock(mutex);
258         return (ret);
259 }
260
261 int
262 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
263 {
264
265         return (cond_wait_common(cond, mutex, NULL, 0));
266 }
267
268 __strong_reference(_pthread_cond_wait, _thr_cond_wait);
269
270 int
271 __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
272 {
273
274         return (cond_wait_common(cond, mutex, NULL, 1));
275 }
276
277 int
278 _pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
279                        const struct timespec * abstime)
280 {
281         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
282             abstime->tv_nsec >= 1000000000)
283                 return (EINVAL);
284
285         return (cond_wait_common(cond, mutex, abstime, 0));
286 }
287
288 __strong_reference(_pthread_cond_timedwait, _thr_cond_timedwait);
289
290 int
291 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
292                        const struct timespec *abstime)
293 {
294         if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
295             abstime->tv_nsec >= 1000000000)
296                 return (EINVAL);
297
298         return (cond_wait_common(cond, mutex, abstime, 1));
299 }
300
301 static int
302 cond_signal_common(pthread_cond_t *cond, int broadcast)
303 {
304         struct pthread  *curthread = tls_get_curthread();
305         pthread_cond_t  cv;
306         int             ret = 0;
307
308         /*
309          * If the condition variable is statically initialized, perform dynamic
310          * initialization.
311          */
312         if (__predict_false(*cond == NULL &&
313             (ret = init_static(curthread, cond)) != 0))
314                 return (ret);
315
316         cv = *cond;
317         /* Lock the condition variable structure. */
318         THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
319         if (cv->c_waiters) {
320                 if (!broadcast) {
321                         cv->c_wakeups++;
322                         cv->c_waiters--;
323                         cv->c_seqno++;
324                         _thr_umtx_wake(&cv->c_seqno, 1);
325                 } else {
326                         cv->c_wakeups += cv->c_waiters;
327                         cv->c_waiters = 0;
328                         cv->c_seqno++;
329                         _thr_umtx_wake(&cv->c_seqno, INT_MAX);
330                 }
331         }
332         THR_LOCK_RELEASE(curthread, &cv->c_lock);
333         return (ret);
334 }
335
336 int
337 _pthread_cond_signal(pthread_cond_t * cond)
338 {
339
340         return (cond_signal_common(cond, 0));
341 }
342
343 __strong_reference(_pthread_cond_signal, _thr_cond_signal);
344
345 int
346 _pthread_cond_broadcast(pthread_cond_t * cond)
347 {
348
349         return (cond_signal_common(cond, 1));
350 }
351
352 __strong_reference(_pthread_cond_broadcast, _thr_cond_broadcast);