xref: /dragonfly/lib/libthread_xu/thread/thr_cond.c (revision cf8046a92768d53e67d2533fb51b137d5506248d)
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  */
27 
28 #include "namespace.h"
29 #include <machine/tls.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <pthread.h>
33 #include <limits.h>
34 #include "un-namespace.h"
35 
36 #include "thr_private.h"
37 
38 #ifdef _PTHREADS_DEBUGGING
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/file.h>
42 #endif
43 
44 #define cpu_ccfence()         __asm __volatile("" : : : "memory")
45 
46 umtx_t              _cond_static_lock;
47 
48 #ifdef _PTHREADS_DEBUGGING
49 
50 static
51 void
cond_log(const char * ctl,...)52 cond_log(const char *ctl, ...)
53 {
54           char buf[256];
55           va_list va;
56           size_t len;
57 
58           va_start(va, ctl);
59           len = vsnprintf(buf, sizeof(buf), ctl, va);
60           va_end(va);
61           _thr_log(buf, len);
62 }
63 
64 #else
65 
66 static __inline
67 void
cond_log(const char * ctl __unused,...)68 cond_log(const char *ctl __unused, ...)
69 {
70 }
71 
72 #endif
73 
74 /*
75  * Prototypes
76  */
77 int       __pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex);
78 int       __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
79                                          const struct timespec *abstime);
80 static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
81                                   const struct timespec *abstime, int cancel);
82 static int cond_signal_common(pthread_cond_t *cond, int broadcast);
83 
84 static int
cond_init(pthread_cond_t * cond,const pthread_condattr_t * cond_attr)85 cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr)
86 {
87           pthread_cond_t pcond;
88           int rval = 0;
89 
90           pcond = __malloc(sizeof(struct __pthread_cond_s));
91           if (pcond == NULL) {
92                     rval = ENOMEM;
93           } else {
94                     /*
95                      * Initialise the condition variable structure:
96                      */
97                     _thr_umtx_init(&pcond->c_lock);
98                     if (cond_attr == NULL || *cond_attr == NULL) {
99                               pcond->c_pshared = 0;
100                               pcond->c_clockid = CLOCK_REALTIME;
101                     } else {
102                               pcond->c_pshared = (*cond_attr)->c_pshared;
103                               pcond->c_clockid = (*cond_attr)->c_clockid;
104                     }
105                     TAILQ_INIT(&pcond->c_waitlist);
106                     *cond = pcond;
107           }
108           /* Return the completion status: */
109           return (rval);
110 }
111 
112 #if 0
113 void
114 _cond_reinit(pthread_cond_t cond)
115 {
116           if (cond) {
117                     _thr_umtx_init(&cond->c_lock);
118 #if 0
119                     /* retain state */
120                     cond->c_pshared = 0;
121                     cond->c_clockid = CLOCK_REALTIME;
122 #endif
123                     TAILQ_INIT(&cond->c_waitlist);
124           }
125 }
126 #endif
127 
128 static int
init_static(pthread_t thread,pthread_cond_t * cond)129 init_static(pthread_t thread, pthread_cond_t *cond)
130 {
131           int ret;
132 
133           THR_LOCK_ACQUIRE(thread, &_cond_static_lock);
134 
135           if (*cond == NULL)
136                     ret = cond_init(cond, NULL);
137           else
138                     ret = 0;
139 
140           THR_LOCK_RELEASE(thread, &_cond_static_lock);
141 
142           return (ret);
143 }
144 
145 int
_pthread_cond_init(pthread_cond_t * __restrict cond,const pthread_condattr_t * __restrict cond_attr)146 _pthread_cond_init(pthread_cond_t * __restrict cond,
147     const pthread_condattr_t * __restrict cond_attr)
148 {
149           *cond = NULL;
150           return cond_init(cond, cond_attr);
151 }
152 
153 int
_pthread_cond_destroy(pthread_cond_t * cond)154 _pthread_cond_destroy(pthread_cond_t *cond)
155 {
156           pthread_cond_t      cv;
157           pthread_t curthread = tls_get_curthread();
158           int                 rval = 0;
159 
160           if (cond == NULL) {
161                     rval = EINVAL;
162           } else if (*cond == NULL) {
163                     rval = 0;
164           } else {
165                     /* Lock the condition variable structure: */
166                     THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock);
167                     if (TAILQ_FIRST(&(*cond)->c_waitlist)) {
168                               THR_LOCK_RELEASE(curthread, &(*cond)->c_lock);
169                               return (EBUSY);
170                     }
171 
172                     /*
173                      * NULL the caller's pointer now that the condition
174                      * variable has been destroyed:
175                      */
176                     cv = *cond;
177                     *cond = NULL;
178 
179                     /* Unlock the condition variable structure: */
180                     THR_LOCK_RELEASE(curthread, &cv->c_lock);
181 
182                     /* Free the cond lock structure: */
183 
184                     /*
185                      * Free the memory allocated for the condition
186                      * variable structure:
187                      */
188                     __free(cv);
189 
190           }
191           /* Return the completion status: */
192           return (rval);
193 }
194 
195 struct cond_cancel_info {
196           TAILQ_ENTRY(cond_cancel_info) entry;
197           pthread_mutex_t     *mutex;
198           pthread_cond_t      *cond;
199           int                 count;
200           int                 queued;
201 };
202 
203 static void
cond_cancel_handler(void * arg)204 cond_cancel_handler(void *arg)
205 {
206           pthread_t curthread = tls_get_curthread();
207           struct cond_cancel_info *info = (struct cond_cancel_info *)arg;
208           pthread_cond_t cv;
209 
210           cv = *info->cond;
211           THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
212           cond_log("cond_cancel %p\n", cv);
213 
214           if (info->queued) {
215                     info->queued = 0;
216                     cond_log("cond_cancel %p: info %p\n", cv, info);
217                     TAILQ_REMOVE(&cv->c_waitlist, info, entry);
218                     _thr_umtx_wake(&info->queued, 0);
219           }
220           THR_LOCK_RELEASE(curthread, &cv->c_lock);
221 
222           /* _mutex_cv_lock(info->mutex, info->count); */
223 }
224 
225 /*
226  * Wait for pthread_cond_t to be signaled.
227  *
228  * NOTE: EINTR is ignored and may not be returned by this function.
229  */
230 static int
cond_wait_common(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime,int cancel)231 cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex,
232                      const struct timespec *abstime, int cancel)
233 {
234           pthread_t curthread = tls_get_curthread();
235           struct timespec ts, ts2, *tsp;
236           struct cond_cancel_info info;
237           pthread_cond_t  cv;
238           int                 oldcancel;
239           int                 ret;
240 
241           /*
242            * If the condition variable is statically initialized,
243            * perform the dynamic initialization:
244            */
245           cond_log("cond_wait_common %p on mutex %p info %p\n",
246                     *cond, *mutex, &info);
247           if (__predict_false(*cond == NULL &&
248               (ret = init_static(curthread, cond)) != 0)) {
249                     cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret);
250                     return (ret);
251           }
252 
253           cv = *cond;
254           THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
255           ret = _mutex_cv_unlock(mutex, &info.count);
256           if (ret) {
257                     cond_log("cond_wait_common %p (failedB %d)\n", cv, ret);
258                     THR_LOCK_RELEASE(curthread, &cv->c_lock);
259                     return ret;
260           }
261 
262           cpu_ccfence();
263           info.mutex = mutex;
264           info.cond  = cond;
265           info.queued = 1;
266           TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry);
267 
268           /*
269            * loop if we have never been told to wake up
270            * or we lost a race.
271            */
272           while (info.queued) {
273                     THR_LOCK_RELEASE(curthread, &cv->c_lock);
274 
275                     if (abstime != NULL) {
276                               clock_gettime(cv->c_clockid, &ts);
277                               timespecsub(abstime, &ts, &ts2);
278                               tsp = &ts2;
279                     } else {
280                               tsp = NULL;
281                     }
282 
283                     if (cancel) {
284                               THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info);
285                               oldcancel = _thr_cancel_enter(curthread);
286                               ret = _thr_umtx_wait(&info.queued, 1, tsp,
287                                                        cv->c_clockid);
288                               _thr_cancel_leave(curthread, oldcancel);
289                               THR_CLEANUP_POP(curthread, 0);
290                     } else {
291                               ret = _thr_umtx_wait(&info.queued, 1, tsp,
292                                                        cv->c_clockid);
293                     }
294 
295                     /*
296                      * Ignore EINTR.  Make sure ret is 0 if not ETIMEDOUT.
297                      */
298                     THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
299                     if (abstime != NULL && ret == ETIMEDOUT)
300                               break;
301                     cpu_ccfence();
302           }
303 
304           if (info.queued) {
305                     info.queued = 0;
306                     TAILQ_REMOVE(&cv->c_waitlist, &info, entry);
307                     ret = ETIMEDOUT;
308           } else {
309                     ret = 0;
310           }
311           THR_LOCK_RELEASE(curthread, &cv->c_lock);
312 
313           cond_log("cond_wait_common %p (doneA)\n", cv);
314           _mutex_cv_lock(mutex, info.count);
315 
316           if (ret)
317                     cond_log("cond_wait_common %p (failed %d)\n", cv, ret);
318           else
319                     cond_log("cond_wait_common %p (doneB)\n", cv);
320 
321           return (ret);
322 }
323 
324 int
_pthread_cond_wait(pthread_cond_t * cond,pthread_mutex_t * mutex)325 _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
326 {
327           return (cond_wait_common(cond, mutex, NULL, 0));
328 }
329 
330 int
__pthread_cond_wait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex)331 __pthread_cond_wait(pthread_cond_t * __restrict cond,
332     pthread_mutex_t * __restrict mutex)
333 {
334           return (cond_wait_common(cond, mutex, NULL, 1));
335 }
336 
337 int
_pthread_cond_timedwait(pthread_cond_t * __restrict cond,pthread_mutex_t * __restrict mutex,const struct timespec * __restrict abstime)338 _pthread_cond_timedwait(pthread_cond_t * __restrict cond,
339     pthread_mutex_t * __restrict mutex,
340     const struct timespec * __restrict abstime)
341 {
342           if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
343               abstime->tv_nsec >= 1000000000)
344                     return (EINVAL);
345 
346           return (cond_wait_common(cond, mutex, abstime, 0));
347 }
348 
349 int
__pthread_cond_timedwait(pthread_cond_t * cond,pthread_mutex_t * mutex,const struct timespec * abstime)350 __pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,
351                            const struct timespec *abstime)
352 {
353           if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 ||
354               abstime->tv_nsec >= 1000000000)
355                     return (EINVAL);
356 
357           return (cond_wait_common(cond, mutex, abstime, 1));
358 }
359 
360 static int
cond_signal_common(pthread_cond_t * cond,int broadcast)361 cond_signal_common(pthread_cond_t *cond, int broadcast)
362 {
363           pthread_t curthread = tls_get_curthread();
364           struct cond_cancel_info *info;
365           pthread_cond_t      cv;
366           int                 ret = 0;
367 
368           cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast);
369 
370           /*
371            * If the condition variable is statically initialized, perform dynamic
372            * initialization.
373            */
374           if (__predict_false(*cond == NULL &&
375                                   (ret = init_static(curthread, cond)) != 0)) {
376                     cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret);
377                     return (ret);
378           }
379 
380           cv = *cond;
381           /* Lock the condition variable structure. */
382           THR_LOCK_ACQUIRE(curthread, &cv->c_lock);
383           while ((info = TAILQ_FIRST(&cv->c_waitlist)) != NULL) {
384                     info->queued = 0;
385                     TAILQ_REMOVE(&cv->c_waitlist, info, entry);
386                     cond_log("cond_signal_common %p: wakeup %p\n", *cond, info);
387                     _thr_umtx_wake(&info->queued, 0);
388                     if (broadcast == 0)
389                               break;
390           }
391           THR_LOCK_RELEASE(curthread, &cv->c_lock);
392 
393           if (ret)
394                     cond_log("cond_signal_common %p (failedB %d)\n", *cond, ret);
395           else
396                     cond_log("cond_signal_common %p (done)\n", *cond);
397 
398           return (ret);
399 }
400 
401 int
_pthread_cond_signal(pthread_cond_t * cond)402 _pthread_cond_signal(pthread_cond_t * cond)
403 {
404           return (cond_signal_common(cond, 0));
405 }
406 
407 int
_pthread_cond_broadcast(pthread_cond_t * cond)408 _pthread_cond_broadcast(pthread_cond_t * cond)
409 {
410           return (cond_signal_common(cond, 1));
411 }
412 
413 /*
414  * Double underscore versions are cancellation points.  Single underscore
415  * versions are not and are provided for libc internal usage (which
416  * shouldn't introduce cancellation points).
417  */
418 __strong_reference(__pthread_cond_wait, pthread_cond_wait);
419 __strong_reference(__pthread_cond_timedwait, pthread_cond_timedwait);
420 
421 __strong_reference(_pthread_cond_init, pthread_cond_init);
422 __strong_reference(_pthread_cond_destroy, pthread_cond_destroy);
423 __strong_reference(_pthread_cond_signal, pthread_cond_signal);
424 __strong_reference(_pthread_cond_broadcast, pthread_cond_broadcast);
425