Commit | Line | Data |
---|---|---|
71b3fa15 DX |
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 | * | |
71b3fa15 DX |
26 | */ |
27 | ||
fc71f871 | 28 | #include "namespace.h" |
9e2ee207 | 29 | #include <machine/tls.h> |
71b3fa15 | 30 | #include <stdlib.h> |
71b3fa15 DX |
31 | #include <string.h> |
32 | #include <pthread.h> | |
33 | #include <limits.h> | |
fc71f871 | 34 | #include "un-namespace.h" |
71b3fa15 DX |
35 | |
36 | #include "thr_private.h" | |
37 | ||
fcaa7a3a MD |
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 | ||
e8382b15 DX |
46 | umtx_t _cond_static_lock; |
47 | ||
fcaa7a3a MD |
48 | #ifdef _PTHREADS_DEBUGGING |
49 | ||
50 | static | |
51 | void | |
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 | |
68 | cond_log(const char *ctl __unused, ...) | |
69 | { | |
70 | } | |
71 | ||
72 | #endif | |
73 | ||
71b3fa15 DX |
74 | /* |
75 | * Prototypes | |
76 | */ | |
19451dc5 | 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); | |
71b3fa15 | 80 | static int cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, |
19451dc5 | 81 | const struct timespec *abstime, int cancel); |
71b3fa15 DX |
82 | static int cond_signal_common(pthread_cond_t *cond, int broadcast); |
83 | ||
71b3fa15 DX |
84 | static int |
85 | cond_init(pthread_cond_t *cond, const pthread_condattr_t *cond_attr) | |
86 | { | |
19451dc5 | 87 | pthread_cond_t pcond; |
88 | int rval = 0; | |
71b3fa15 | 89 | |
cf8046a9 | 90 | pcond = __malloc(sizeof(struct __pthread_cond_s)); |
e7bf3f77 | 91 | if (pcond == NULL) { |
71b3fa15 DX |
92 | rval = ENOMEM; |
93 | } else { | |
94 | /* | |
95 | * Initialise the condition variable structure: | |
96 | */ | |
97 | _thr_umtx_init(&pcond->c_lock); | |
71b3fa15 DX |
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 | } | |
fcaa7a3a | 105 | TAILQ_INIT(&pcond->c_waitlist); |
71b3fa15 DX |
106 | *cond = pcond; |
107 | } | |
108 | /* Return the completion status: */ | |
109 | return (rval); | |
110 | } | |
111 | ||
fcaa7a3a MD |
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 | ||
71b3fa15 | 128 | static int |
940be950 | 129 | init_static(pthread_t thread, pthread_cond_t *cond) |
71b3fa15 DX |
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 | |
d33005aa SW |
146 | _pthread_cond_init(pthread_cond_t * __restrict cond, |
147 | const pthread_condattr_t * __restrict cond_attr) | |
71b3fa15 DX |
148 | { |
149 | *cond = NULL; | |
150 | return cond_init(cond, cond_attr); | |
151 | } | |
152 | ||
153 | int | |
154 | _pthread_cond_destroy(pthread_cond_t *cond) | |
155 | { | |
940be950 | 156 | pthread_cond_t cv; |
157 | pthread_t curthread = tls_get_curthread(); | |
158 | int rval = 0; | |
71b3fa15 | 159 | |
e7bf3f77 | 160 | if (cond == NULL) { |
71b3fa15 | 161 | rval = EINVAL; |
e7bf3f77 | 162 | } else if (*cond == NULL) { |
146da5fc | 163 | rval = 0; |
e7bf3f77 | 164 | } else { |
71b3fa15 DX |
165 | /* Lock the condition variable structure: */ |
166 | THR_LOCK_ACQUIRE(curthread, &(*cond)->c_lock); | |
fcaa7a3a | 167 | if (TAILQ_FIRST(&(*cond)->c_waitlist)) { |
71b3fa15 DX |
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 | */ | |
e7bf3f77 | 188 | __free(cv); |
71b3fa15 DX |
189 | |
190 | } | |
191 | /* Return the completion status: */ | |
192 | return (rval); | |
193 | } | |
194 | ||
fcaa7a3a MD |
195 | struct cond_cancel_info { |
196 | TAILQ_ENTRY(cond_cancel_info) entry; | |
71b3fa15 DX |
197 | pthread_mutex_t *mutex; |
198 | pthread_cond_t *cond; | |
19451dc5 | 199 | int count; |
fcaa7a3a | 200 | int queued; |
71b3fa15 DX |
201 | }; |
202 | ||
203 | static void | |
204 | cond_cancel_handler(void *arg) | |
205 | { | |
940be950 | 206 | pthread_t curthread = tls_get_curthread(); |
a8851a0f | 207 | struct cond_cancel_info *info = (struct cond_cancel_info *)arg; |
71b3fa15 DX |
208 | pthread_cond_t cv; |
209 | ||
fcaa7a3a | 210 | cv = *info->cond; |
71b3fa15 | 211 | THR_LOCK_ACQUIRE(curthread, &cv->c_lock); |
fcaa7a3a MD |
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); | |
71b3fa15 DX |
219 | } |
220 | THR_LOCK_RELEASE(curthread, &cv->c_lock); | |
221 | ||
fcaa7a3a | 222 | /* _mutex_cv_lock(info->mutex, info->count); */ |
71b3fa15 DX |
223 | } |
224 | ||
fcaa7a3a MD |
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 | */ | |
71b3fa15 DX |
230 | static int |
231 | cond_wait_common(pthread_cond_t *cond, pthread_mutex_t *mutex, | |
fcaa7a3a | 232 | const struct timespec *abstime, int cancel) |
71b3fa15 | 233 | { |
940be950 | 234 | pthread_t curthread = tls_get_curthread(); |
71b3fa15 | 235 | struct timespec ts, ts2, *tsp; |
a8851a0f | 236 | struct cond_cancel_info info; |
71b3fa15 | 237 | pthread_cond_t cv; |
71b3fa15 | 238 | int oldcancel; |
fcaa7a3a | 239 | int ret; |
71b3fa15 DX |
240 | |
241 | /* | |
242 | * If the condition variable is statically initialized, | |
243 | * perform the dynamic initialization: | |
244 | */ | |
fcaa7a3a MD |
245 | cond_log("cond_wait_common %p on mutex %p info %p\n", |
246 | *cond, *mutex, &info); | |
71b3fa15 | 247 | if (__predict_false(*cond == NULL && |
fcaa7a3a MD |
248 | (ret = init_static(curthread, cond)) != 0)) { |
249 | cond_log("cond_wait_common %p (failedA %d)\n", *cond, ret); | |
71b3fa15 | 250 | return (ret); |
fcaa7a3a | 251 | } |
71b3fa15 DX |
252 | |
253 | cv = *cond; | |
254 | THR_LOCK_ACQUIRE(curthread, &cv->c_lock); | |
a8851a0f | 255 | ret = _mutex_cv_unlock(mutex, &info.count); |
71b3fa15 | 256 | if (ret) { |
fcaa7a3a | 257 | cond_log("cond_wait_common %p (failedB %d)\n", cv, ret); |
71b3fa15 | 258 | THR_LOCK_RELEASE(curthread, &cv->c_lock); |
fcaa7a3a | 259 | return ret; |
71b3fa15 | 260 | } |
fcaa7a3a MD |
261 | |
262 | cpu_ccfence(); | |
a8851a0f DX |
263 | info.mutex = mutex; |
264 | info.cond = cond; | |
fcaa7a3a MD |
265 | info.queued = 1; |
266 | TAILQ_INSERT_TAIL(&cv->c_waitlist, &info, entry); | |
3db51647 MD |
267 | |
268 | /* | |
269 | * loop if we have never been told to wake up | |
270 | * or we lost a race. | |
271 | */ | |
fcaa7a3a | 272 | while (info.queued) { |
71b3fa15 DX |
273 | THR_LOCK_RELEASE(curthread, &cv->c_lock); |
274 | ||
275 | if (abstime != NULL) { | |
276 | clock_gettime(cv->c_clockid, &ts); | |
ce96aca2 | 277 | timespecsub(abstime, &ts, &ts2); |
71b3fa15 | 278 | tsp = &ts2; |
fcaa7a3a | 279 | } else { |
71b3fa15 | 280 | tsp = NULL; |
fcaa7a3a | 281 | } |
71b3fa15 DX |
282 | |
283 | if (cancel) { | |
a8851a0f | 284 | THR_CLEANUP_PUSH(curthread, cond_cancel_handler, &info); |
71b3fa15 | 285 | oldcancel = _thr_cancel_enter(curthread); |
fcaa7a3a MD |
286 | ret = _thr_umtx_wait(&info.queued, 1, tsp, |
287 | cv->c_clockid); | |
71b3fa15 DX |
288 | _thr_cancel_leave(curthread, oldcancel); |
289 | THR_CLEANUP_POP(curthread, 0); | |
290 | } else { | |
fcaa7a3a MD |
291 | ret = _thr_umtx_wait(&info.queued, 1, tsp, |
292 | cv->c_clockid); | |
71b3fa15 DX |
293 | } |
294 | ||
fcaa7a3a MD |
295 | /* |
296 | * Ignore EINTR. Make sure ret is 0 if not ETIMEDOUT. | |
297 | */ | |
71b3fa15 | 298 | THR_LOCK_ACQUIRE(curthread, &cv->c_lock); |
71b3fa15 DX |
299 | if (abstime != NULL && ret == ETIMEDOUT) |
300 | break; | |
fcaa7a3a | 301 | cpu_ccfence(); |
71b3fa15 | 302 | } |
fcaa7a3a MD |
303 | |
304 | if (info.queued) { | |
305 | info.queued = 0; | |
306 | TAILQ_REMOVE(&cv->c_waitlist, &info, entry); | |
307 | ret = ETIMEDOUT; | |
308 | } else { | |
3db51647 | 309 | ret = 0; |
fcaa7a3a | 310 | } |
71b3fa15 | 311 | THR_LOCK_RELEASE(curthread, &cv->c_lock); |
fcaa7a3a MD |
312 | |
313 | cond_log("cond_wait_common %p (doneA)\n", cv); | |
a8851a0f | 314 | _mutex_cv_lock(mutex, info.count); |
fcaa7a3a MD |
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 | ||
71b3fa15 DX |
321 | return (ret); |
322 | } | |
323 | ||
324 | int | |
325 | _pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) | |
326 | { | |
36750816 | 327 | return (cond_wait_common(cond, mutex, NULL, 0)); |
71b3fa15 DX |
328 | } |
329 | ||
71b3fa15 | 330 | int |
d33005aa SW |
331 | __pthread_cond_wait(pthread_cond_t * __restrict cond, |
332 | pthread_mutex_t * __restrict mutex) | |
71b3fa15 | 333 | { |
36750816 | 334 | return (cond_wait_common(cond, mutex, NULL, 1)); |
71b3fa15 DX |
335 | } |
336 | ||
337 | int | |
d33005aa SW |
338 | _pthread_cond_timedwait(pthread_cond_t * __restrict cond, |
339 | pthread_mutex_t * __restrict mutex, | |
340 | const struct timespec * __restrict abstime) | |
71b3fa15 DX |
341 | { |
342 | if (abstime == NULL || abstime->tv_sec < 0 || abstime->tv_nsec < 0 || | |
343 | abstime->tv_nsec >= 1000000000) | |
344 | return (EINVAL); | |
345 | ||
36750816 | 346 | return (cond_wait_common(cond, mutex, abstime, 0)); |
71b3fa15 DX |
347 | } |
348 | ||
71b3fa15 DX |
349 | int |
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 | ||
36750816 | 357 | return (cond_wait_common(cond, mutex, abstime, 1)); |
71b3fa15 DX |
358 | } |
359 | ||
360 | static int | |
361 | cond_signal_common(pthread_cond_t *cond, int broadcast) | |
362 | { | |
940be950 | 363 | pthread_t curthread = tls_get_curthread(); |
fcaa7a3a | 364 | struct cond_cancel_info *info; |
71b3fa15 DX |
365 | pthread_cond_t cv; |
366 | int ret = 0; | |
367 | ||
fcaa7a3a MD |
368 | cond_log("cond_signal_common %p broad=%d\n", *cond, broadcast); |
369 | ||
71b3fa15 DX |
370 | /* |
371 | * If the condition variable is statically initialized, perform dynamic | |
372 | * initialization. | |
373 | */ | |
374 | if (__predict_false(*cond == NULL && | |
fcaa7a3a MD |
375 | (ret = init_static(curthread, cond)) != 0)) { |
376 | cond_log("cond_signal_common %p (failedA %d)\n", *cond, ret); | |
71b3fa15 | 377 | return (ret); |
fcaa7a3a | 378 | } |
71b3fa15 DX |
379 | |
380 | cv = *cond; | |
381 | /* Lock the condition variable structure. */ | |
382 | THR_LOCK_ACQUIRE(curthread, &cv->c_lock); | |
fcaa7a3a MD |
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; | |
71b3fa15 DX |
390 | } |
391 | THR_LOCK_RELEASE(curthread, &cv->c_lock); | |
fcaa7a3a MD |
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 | ||
71b3fa15 DX |
398 | return (ret); |
399 | } | |
400 | ||
401 | int | |
402 | _pthread_cond_signal(pthread_cond_t * cond) | |
403 | { | |
36750816 | 404 | return (cond_signal_common(cond, 0)); |
71b3fa15 DX |
405 | } |
406 | ||
71b3fa15 DX |
407 | int |
408 | _pthread_cond_broadcast(pthread_cond_t * cond) | |
409 | { | |
36750816 | 410 | return (cond_signal_common(cond, 1)); |
71b3fa15 | 411 | } |
5a1048c8 DX |
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); |