1 /* $OpenBSD: rthread_cond.c,v 1.6 2024/01/10 04:28:43 cheloha Exp $ */
2 /*
3 * Copyright (c) 2017 Martin Pieuchot <mpi@openbsd.org>
4 * Copyright (c) 2012 Philip Guenther <guenther@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <errno.h>
20 #include <pthread.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <unistd.h>
25
26 #include "rthread.h"
27 #include "cancel.h"
28 #include "synch.h"
29
30 int
pthread_cond_init(pthread_cond_t * condp,const pthread_condattr_t * attr)31 pthread_cond_init(pthread_cond_t *condp, const pthread_condattr_t *attr)
32 {
33 pthread_cond_t cond;
34
35 cond = calloc(1, sizeof(*cond));
36 if (cond == NULL)
37 return (ENOMEM);
38
39 if (attr == NULL)
40 cond->clock = CLOCK_REALTIME;
41 else
42 cond->clock = (*attr)->ca_clock;
43 *condp = cond;
44
45 return (0);
46 }
47 DEF_STRONG(pthread_cond_init);
48
49 int
pthread_cond_destroy(pthread_cond_t * condp)50 pthread_cond_destroy(pthread_cond_t *condp)
51 {
52 pthread_cond_t cond;
53
54 cond = *condp;
55
56 if (cond != NULL) {
57 if (cond->mutex != NULL) {
58 #define MSG "pthread_cond_destroy on condvar with waiters!\n"
59 write(2, MSG, sizeof(MSG) - 1);
60 #undef MSG
61 return (EBUSY);
62 }
63 free(cond);
64 }
65 *condp = NULL;
66
67 return (0);
68 }
69
70 int
_rthread_cond_timedwait(pthread_cond_t cond,pthread_mutex_t * mutexp,const struct timespec * abs)71 _rthread_cond_timedwait(pthread_cond_t cond, pthread_mutex_t *mutexp,
72 const struct timespec *abs)
73 {
74 struct pthread_mutex *mutex = (struct pthread_mutex *)*mutexp;
75 struct tib *tib = TIB_GET();
76 pthread_t self = tib->tib_thread;
77 int error, rv = 0, canceled = 0, mutex_count = 0;
78 clockid_t clock = cond->clock;
79 int seq = cond->seq;
80 PREP_CANCEL_POINT(tib);
81
82 _rthread_debug(5, "%p: cond_timed %p,%p (%p)\n", self,
83 (void *)cond, (void *)mutex, (void *)mutex->owner);
84
85 ENTER_DELAYED_CANCEL_POINT(tib, self);
86
87 #if notyet
88 /* mark the condvar as being associated with this mutex */
89 if (cond->mutex == NULL)
90 atomic_cas_ptr(&cond->mutex, NULL, mutex);
91
92 if (cond->mutex != mutex) {
93 LEAVE_CANCEL_POINT_INNER(tib, 1);
94 return (EINVAL);
95 }
96 #endif
97
98 /* snag the count in case this is a recursive mutex */
99 if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
100 mutex_count = mutex->count;
101
102 pthread_mutex_unlock(mutexp);
103
104 do {
105 /* If ``seq'' wraps you deserve to lose a signal. */
106 error = _twait(&cond->seq, seq, clock, abs);
107 /*
108 * If we took a normal signal (not from cancellation) then
109 * we should just go back to sleep without changing state
110 * (timeouts, etc).
111 */
112 } while ((error == EINTR) &&
113 (tib->tib_canceled == 0 || (tib->tib_cantcancel & CANCEL_DISABLED)));
114
115 /* if timeout or canceled, make note of that */
116 if (error == ETIMEDOUT)
117 rv = ETIMEDOUT;
118 else if (error == EINTR)
119 canceled = 1;
120
121 pthread_mutex_lock(mutexp);
122
123 /* restore the mutex's count */
124 if (mutex->type == PTHREAD_MUTEX_RECURSIVE)
125 mutex->count = mutex_count;
126
127 LEAVE_CANCEL_POINT_INNER(tib, canceled);
128
129 return rv;
130 }
131
132 int
pthread_cond_timedwait(pthread_cond_t * condp,pthread_mutex_t * mutexp,const struct timespec * abs)133 pthread_cond_timedwait(pthread_cond_t *condp, pthread_mutex_t *mutexp,
134 const struct timespec *abs)
135 {
136 pthread_cond_t cond;
137 int error;
138
139 if (*condp == NULL) {
140 if ((error = pthread_cond_init(condp, NULL)))
141 return (error);
142 }
143
144 cond = *condp;
145 if (abs == NULL || abs->tv_nsec < 0 || abs->tv_nsec >= 1000000000)
146 return (EINVAL);
147
148 return (_rthread_cond_timedwait(cond, mutexp, abs));
149 }
150
151 int
pthread_cond_wait(pthread_cond_t * condp,pthread_mutex_t * mutexp)152 pthread_cond_wait(pthread_cond_t *condp, pthread_mutex_t *mutexp)
153 {
154 pthread_cond_t cond;
155 int error;
156
157 if (*condp == NULL) {
158 if ((error = pthread_cond_init(condp, NULL)))
159 return (error);
160 }
161
162 cond = *condp;
163 return (_rthread_cond_timedwait(cond, mutexp, NULL));
164 }
165
166 int
pthread_cond_signal(pthread_cond_t * condp)167 pthread_cond_signal(pthread_cond_t *condp)
168 {
169 pthread_cond_t cond;
170 int count;
171
172 if (*condp == NULL)
173 return (0);
174
175 cond = *condp;
176
177 atomic_inc_int(&cond->seq);
178 count = _wake(&cond->seq, 1);
179
180 _rthread_debug(5, "%p: cond_signal %p, %d awaken\n", pthread_self(),
181 (void *)cond, count);
182
183 return (0);
184 }
185
186 int
pthread_cond_broadcast(pthread_cond_t * condp)187 pthread_cond_broadcast(pthread_cond_t *condp)
188 {
189 pthread_cond_t cond;
190 int count;
191
192 if (*condp == NULL)
193 return (0);
194
195 cond = *condp;
196
197 atomic_inc_int(&cond->seq);
198 #if notyet
199 count = _requeue(&cond->seq, 1, INT_MAX, &cond->mutex->lock);
200 #else
201 count = _wake(&cond->seq, INT_MAX);
202 #endif
203
204 _rthread_debug(5, "%p: cond_broadcast %p, %d awaken\n", pthread_self(),
205 (void *)cond, count);
206
207 return (0);
208 }
209