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