1 /*
2 * Copyright (c) 2017 Intel Corporation
3 * Copyright (c) 2023 Meta
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #include "posix_internal.h"
9
10 #include <zephyr/init.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/logging/log.h>
13 #include <zephyr/posix/pthread.h>
14 #include <zephyr/sys/bitarray.h>
15
16 LOG_MODULE_REGISTER(pthread_cond, CONFIG_PTHREAD_COND_LOG_LEVEL);
17
18 int64_t timespec_to_timeoutms(const struct timespec *abstime);
19
20 __pinned_bss
21 static struct k_condvar posix_cond_pool[CONFIG_MAX_PTHREAD_COND_COUNT];
22
23 SYS_BITARRAY_DEFINE_STATIC(posix_cond_bitarray, CONFIG_MAX_PTHREAD_COND_COUNT);
24
25 /*
26 * We reserve the MSB to mark a pthread_cond_t as initialized (from the
27 * perspective of the application). With a linear space, this means that
28 * the theoretical pthread_cond_t range is [0,2147483647].
29 */
30 BUILD_ASSERT(CONFIG_MAX_PTHREAD_COND_COUNT < PTHREAD_OBJ_MASK_INIT,
31 "CONFIG_MAX_PTHREAD_COND_COUNT is too high");
32
posix_cond_to_offset(struct k_condvar * cv)33 static inline size_t posix_cond_to_offset(struct k_condvar *cv)
34 {
35 return cv - posix_cond_pool;
36 }
37
to_posix_cond_idx(pthread_cond_t cond)38 static inline size_t to_posix_cond_idx(pthread_cond_t cond)
39 {
40 return mark_pthread_obj_uninitialized(cond);
41 }
42
get_posix_cond(pthread_cond_t cond)43 static struct k_condvar *get_posix_cond(pthread_cond_t cond)
44 {
45 int actually_initialized;
46 size_t bit = to_posix_cond_idx(cond);
47
48 /* if the provided cond does not claim to be initialized, its invalid */
49 if (!is_pthread_obj_initialized(cond)) {
50 LOG_DBG("Cond is uninitialized (%x)", cond);
51 return NULL;
52 }
53
54 /* Mask off the MSB to get the actual bit index */
55 if (sys_bitarray_test_bit(&posix_cond_bitarray, bit, &actually_initialized) < 0) {
56 LOG_DBG("Cond is invalid (%x)", cond);
57 return NULL;
58 }
59
60 if (actually_initialized == 0) {
61 /* The cond claims to be initialized but is actually not */
62 LOG_DBG("Cond claims to be initialized (%x)", cond);
63 return NULL;
64 }
65
66 return &posix_cond_pool[bit];
67 }
68
to_posix_cond(pthread_cond_t * cvar)69 static struct k_condvar *to_posix_cond(pthread_cond_t *cvar)
70 {
71 size_t bit;
72 struct k_condvar *cv;
73
74 if (*cvar != PTHREAD_COND_INITIALIZER) {
75 return get_posix_cond(*cvar);
76 }
77
78 /* Try and automatically associate a posix_cond */
79 if (sys_bitarray_alloc(&posix_cond_bitarray, 1, &bit) < 0) {
80 /* No conds left to allocate */
81 LOG_DBG("Unable to allocate pthread_cond_t");
82 return NULL;
83 }
84
85 /* Record the associated posix_cond in mu and mark as initialized */
86 *cvar = mark_pthread_obj_initialized(bit);
87 cv = &posix_cond_pool[bit];
88
89 return cv;
90 }
91
cond_wait(pthread_cond_t * cond,pthread_mutex_t * mu,k_timeout_t timeout)92 static int cond_wait(pthread_cond_t *cond, pthread_mutex_t *mu, k_timeout_t timeout)
93 {
94 int ret;
95 struct k_mutex *m;
96 struct k_condvar *cv;
97
98 m = to_posix_mutex(mu);
99 cv = to_posix_cond(cond);
100 if (cv == NULL || m == NULL) {
101 return EINVAL;
102 }
103
104 LOG_DBG("Waiting on cond %p with timeout %llx", cv, timeout.ticks);
105 ret = k_condvar_wait(cv, m, timeout);
106 if (ret == -EAGAIN) {
107 LOG_DBG("Timeout waiting on cond %p", cv);
108 ret = ETIMEDOUT;
109 } else if (ret < 0) {
110 LOG_DBG("k_condvar_wait() failed: %d", ret);
111 ret = -ret;
112 } else {
113 __ASSERT_NO_MSG(ret == 0);
114 LOG_DBG("Cond %p received signal", cv);
115 }
116
117 return ret;
118 }
119
pthread_cond_signal(pthread_cond_t * cvar)120 int pthread_cond_signal(pthread_cond_t *cvar)
121 {
122 int ret;
123 struct k_condvar *cv;
124
125 cv = to_posix_cond(cvar);
126 if (cv == NULL) {
127 return EINVAL;
128 }
129
130 LOG_DBG("Signaling cond %p", cv);
131 ret = k_condvar_signal(cv);
132 if (ret < 0) {
133 LOG_DBG("k_condvar_signal() failed: %d", ret);
134 return -ret;
135 }
136
137 __ASSERT_NO_MSG(ret == 0);
138
139 return 0;
140 }
141
pthread_cond_broadcast(pthread_cond_t * cvar)142 int pthread_cond_broadcast(pthread_cond_t *cvar)
143 {
144 int ret;
145 struct k_condvar *cv;
146
147 cv = get_posix_cond(*cvar);
148 if (cv == NULL) {
149 return EINVAL;
150 }
151
152 LOG_DBG("Broadcasting on cond %p", cv);
153 ret = k_condvar_broadcast(cv);
154 if (ret < 0) {
155 LOG_DBG("k_condvar_broadcast() failed: %d", ret);
156 return -ret;
157 }
158
159 __ASSERT_NO_MSG(ret >= 0);
160
161 return 0;
162 }
163
pthread_cond_wait(pthread_cond_t * cv,pthread_mutex_t * mut)164 int pthread_cond_wait(pthread_cond_t *cv, pthread_mutex_t *mut)
165 {
166 return cond_wait(cv, mut, K_FOREVER);
167 }
168
pthread_cond_timedwait(pthread_cond_t * cv,pthread_mutex_t * mut,const struct timespec * abstime)169 int pthread_cond_timedwait(pthread_cond_t *cv, pthread_mutex_t *mut, const struct timespec *abstime)
170 {
171 return cond_wait(cv, mut, K_MSEC((int32_t)timespec_to_timeoutms(abstime)));
172 }
173
pthread_cond_init(pthread_cond_t * cvar,const pthread_condattr_t * att)174 int pthread_cond_init(pthread_cond_t *cvar, const pthread_condattr_t *att)
175 {
176 struct k_condvar *cv;
177
178 ARG_UNUSED(att);
179 *cvar = PTHREAD_COND_INITIALIZER;
180
181 /* calls k_condvar_init() */
182 cv = to_posix_cond(cvar);
183 if (cv == NULL) {
184 return ENOMEM;
185 }
186
187 LOG_DBG("Initialized cond %p", cv);
188
189 return 0;
190 }
191
pthread_cond_destroy(pthread_cond_t * cvar)192 int pthread_cond_destroy(pthread_cond_t *cvar)
193 {
194 int err;
195 size_t bit;
196 struct k_condvar *cv;
197
198 cv = get_posix_cond(*cvar);
199 if (cv == NULL) {
200 return EINVAL;
201 }
202
203 bit = posix_cond_to_offset(cv);
204 err = sys_bitarray_free(&posix_cond_bitarray, 1, bit);
205 __ASSERT_NO_MSG(err == 0);
206
207 *cvar = -1;
208
209 LOG_DBG("Destroyed cond %p", cv);
210
211 return 0;
212 }
213
214 __boot_func
pthread_cond_pool_init(void)215 static int pthread_cond_pool_init(void)
216 {
217 int err;
218 size_t i;
219
220 for (i = 0; i < CONFIG_MAX_PTHREAD_COND_COUNT; ++i) {
221 err = k_condvar_init(&posix_cond_pool[i]);
222 __ASSERT_NO_MSG(err == 0);
223 }
224
225 return 0;
226 }
227
pthread_condattr_init(pthread_condattr_t * att)228 int pthread_condattr_init(pthread_condattr_t *att)
229 {
230 __ASSERT_NO_MSG(att != NULL);
231
232 att->clock = CLOCK_MONOTONIC;
233
234 return 0;
235 }
236
pthread_condattr_destroy(pthread_condattr_t * att)237 int pthread_condattr_destroy(pthread_condattr_t *att)
238 {
239 ARG_UNUSED(att);
240
241 return 0;
242 }
243
pthread_condattr_getclock(const pthread_condattr_t * ZRESTRICT att,clockid_t * ZRESTRICT clock_id)244 int pthread_condattr_getclock(const pthread_condattr_t *ZRESTRICT att,
245 clockid_t *ZRESTRICT clock_id)
246 {
247 *clock_id = att->clock;
248
249 return 0;
250 }
251
pthread_condattr_setclock(pthread_condattr_t * att,clockid_t clock_id)252 int pthread_condattr_setclock(pthread_condattr_t *att, clockid_t clock_id)
253 {
254 if (clock_id != CLOCK_REALTIME && clock_id != CLOCK_MONOTONIC) {
255 return -EINVAL;
256 }
257
258 att->clock = clock_id;
259
260 return 0;
261 }
262 SYS_INIT(pthread_cond_pool_init, PRE_KERNEL_1, 0);
263