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 #include <zephyr/sys/sem.h>
16
17 LOG_MODULE_REGISTER(pthread_mutex, CONFIG_PTHREAD_MUTEX_LOG_LEVEL);
18
19 static SYS_SEM_DEFINE(lock, 1, 1);
20
21 int64_t timespec_to_timeoutms(const struct timespec *abstime);
22
23 #define MUTEX_MAX_REC_LOCK 32767
24
25 /*
26 * Default mutex attrs.
27 */
28 static const struct pthread_mutexattr def_attr = {
29 .type = PTHREAD_MUTEX_DEFAULT,
30 };
31
32 __pinned_bss
33 static struct k_mutex posix_mutex_pool[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
34
35 static uint8_t posix_mutex_type[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
36 SYS_BITARRAY_DEFINE_STATIC(posix_mutex_bitarray, CONFIG_MAX_PTHREAD_MUTEX_COUNT);
37
38 /*
39 * We reserve the MSB to mark a pthread_mutex_t as initialized (from the
40 * perspective of the application). With a linear space, this means that
41 * the theoretical pthread_mutex_t range is [0,2147483647].
42 */
43 BUILD_ASSERT(CONFIG_MAX_PTHREAD_MUTEX_COUNT < PTHREAD_OBJ_MASK_INIT,
44 "CONFIG_MAX_PTHREAD_MUTEX_COUNT is too high");
45
posix_mutex_to_offset(struct k_mutex * m)46 static inline size_t posix_mutex_to_offset(struct k_mutex *m)
47 {
48 return m - posix_mutex_pool;
49 }
50
to_posix_mutex_idx(pthread_mutex_t mut)51 static inline size_t to_posix_mutex_idx(pthread_mutex_t mut)
52 {
53 return mark_pthread_obj_uninitialized(mut);
54 }
55
get_posix_mutex(pthread_mutex_t mu)56 static struct k_mutex *get_posix_mutex(pthread_mutex_t mu)
57 {
58 int actually_initialized;
59 size_t bit = to_posix_mutex_idx(mu);
60
61 /* if the provided mutex does not claim to be initialized, its invalid */
62 if (!is_pthread_obj_initialized(mu)) {
63 LOG_DBG("Mutex is uninitialized (%x)", mu);
64 return NULL;
65 }
66
67 /* Mask off the MSB to get the actual bit index */
68 if (sys_bitarray_test_bit(&posix_mutex_bitarray, bit, &actually_initialized) < 0) {
69 LOG_DBG("Mutex is invalid (%x)", mu);
70 return NULL;
71 }
72
73 if (actually_initialized == 0) {
74 /* The mutex claims to be initialized but is actually not */
75 LOG_DBG("Mutex claims to be initialized (%x)", mu);
76 return NULL;
77 }
78
79 return &posix_mutex_pool[bit];
80 }
81
to_posix_mutex(pthread_mutex_t * mu)82 struct k_mutex *to_posix_mutex(pthread_mutex_t *mu)
83 {
84 int err;
85 size_t bit;
86 struct k_mutex *m;
87
88 if (*mu != PTHREAD_MUTEX_INITIALIZER) {
89 return get_posix_mutex(*mu);
90 }
91
92 /* Try and automatically associate a posix_mutex */
93 if (sys_bitarray_alloc(&posix_mutex_bitarray, 1, &bit) < 0) {
94 LOG_DBG("Unable to allocate pthread_mutex_t");
95 return NULL;
96 }
97
98 /* Record the associated posix_mutex in mu and mark as initialized */
99 *mu = mark_pthread_obj_initialized(bit);
100
101 /* Initialize the posix_mutex */
102 m = &posix_mutex_pool[bit];
103
104 err = k_mutex_init(m);
105 __ASSERT_NO_MSG(err == 0);
106
107 return m;
108 }
109
acquire_mutex(pthread_mutex_t * mu,k_timeout_t timeout)110 static int acquire_mutex(pthread_mutex_t *mu, k_timeout_t timeout)
111 {
112 int type = -1;
113 size_t bit = -1;
114 int ret = EINVAL;
115 size_t lock_count = -1;
116 struct k_mutex *m = NULL;
117 struct k_thread *owner = NULL;
118
119 SYS_SEM_LOCK(&lock) {
120 m = to_posix_mutex(mu);
121 if (m == NULL) {
122 ret = EINVAL;
123 SYS_SEM_LOCK_BREAK;
124 }
125
126 LOG_DBG("Locking mutex %p with timeout %llx", m, timeout.ticks);
127
128 ret = 0;
129 bit = posix_mutex_to_offset(m);
130 type = posix_mutex_type[bit];
131 owner = m->owner;
132 lock_count = m->lock_count;
133 }
134
135 if (ret != 0) {
136 goto handle_error;
137 }
138
139 if (owner == k_current_get()) {
140 switch (type) {
141 case PTHREAD_MUTEX_NORMAL:
142 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
143 LOG_DBG("Timeout locking mutex %p", m);
144 ret = EBUSY;
145 break;
146 }
147 /* On most POSIX systems, this usually results in an infinite loop */
148 LOG_DBG("Attempt to relock non-recursive mutex %p", m);
149 do {
150 (void)k_sleep(K_FOREVER);
151 } while (true);
152 CODE_UNREACHABLE;
153 break;
154 case PTHREAD_MUTEX_RECURSIVE:
155 if (lock_count >= MUTEX_MAX_REC_LOCK) {
156 LOG_DBG("Mutex %p locked recursively too many times", m);
157 ret = EAGAIN;
158 }
159 break;
160 case PTHREAD_MUTEX_ERRORCHECK:
161 LOG_DBG("Attempt to recursively lock non-recursive mutex %p", m);
162 ret = EDEADLK;
163 break;
164 default:
165 __ASSERT(false, "invalid pthread type %d", type);
166 ret = EINVAL;
167 break;
168 }
169 }
170
171 if (ret == 0) {
172 ret = k_mutex_lock(m, timeout);
173 if (ret == -EAGAIN) {
174 LOG_DBG("Timeout locking mutex %p", m);
175 /*
176 * special quirk - k_mutex_lock() returns EAGAIN if a timeout occurs, but
177 * for pthreads, that means something different
178 */
179 ret = ETIMEDOUT;
180 }
181 }
182
183 handle_error:
184 if (ret < 0) {
185 LOG_DBG("k_mutex_unlock() failed: %d", ret);
186 ret = -ret;
187 }
188
189 if (ret == 0) {
190 LOG_DBG("Locked mutex %p", m);
191 }
192
193 return ret;
194 }
195
196 /**
197 * @brief Lock POSIX mutex with non-blocking call.
198 *
199 * See IEEE 1003.1
200 */
pthread_mutex_trylock(pthread_mutex_t * m)201 int pthread_mutex_trylock(pthread_mutex_t *m)
202 {
203 return acquire_mutex(m, K_NO_WAIT);
204 }
205
206 /**
207 * @brief Lock POSIX mutex with timeout.
208 *
209 *
210 * See IEEE 1003.1
211 */
pthread_mutex_timedlock(pthread_mutex_t * m,const struct timespec * abstime)212 int pthread_mutex_timedlock(pthread_mutex_t *m,
213 const struct timespec *abstime)
214 {
215 int32_t timeout = (int32_t)timespec_to_timeoutms(abstime);
216 return acquire_mutex(m, K_MSEC(timeout));
217 }
218
219 /**
220 * @brief Initialize POSIX mutex.
221 *
222 * See IEEE 1003.1
223 */
pthread_mutex_init(pthread_mutex_t * mu,const pthread_mutexattr_t * _attr)224 int pthread_mutex_init(pthread_mutex_t *mu, const pthread_mutexattr_t *_attr)
225 {
226 size_t bit;
227 struct k_mutex *m;
228 const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
229
230 *mu = PTHREAD_MUTEX_INITIALIZER;
231
232 m = to_posix_mutex(mu);
233 if (m == NULL) {
234 return ENOMEM;
235 }
236
237 bit = posix_mutex_to_offset(m);
238 if (attr == NULL) {
239 posix_mutex_type[bit] = def_attr.type;
240 } else {
241 posix_mutex_type[bit] = attr->type;
242 }
243
244 LOG_DBG("Initialized mutex %p", m);
245
246 return 0;
247 }
248
249
250 /**
251 * @brief Lock POSIX mutex with blocking call.
252 *
253 * See IEEE 1003.1
254 */
pthread_mutex_lock(pthread_mutex_t * m)255 int pthread_mutex_lock(pthread_mutex_t *m)
256 {
257 return acquire_mutex(m, K_FOREVER);
258 }
259
260 /**
261 * @brief Unlock POSIX mutex.
262 *
263 * See IEEE 1003.1
264 */
pthread_mutex_unlock(pthread_mutex_t * mu)265 int pthread_mutex_unlock(pthread_mutex_t *mu)
266 {
267 int ret;
268 struct k_mutex *m;
269
270 m = get_posix_mutex(*mu);
271 if (m == NULL) {
272 return EINVAL;
273 }
274
275 ret = k_mutex_unlock(m);
276 if (ret < 0) {
277 LOG_DBG("k_mutex_unlock() failed: %d", ret);
278 return -ret;
279 }
280
281 __ASSERT_NO_MSG(ret == 0);
282 LOG_DBG("Unlocked mutex %p", m);
283
284 return 0;
285 }
286
287 /**
288 * @brief Destroy POSIX mutex.
289 *
290 * See IEEE 1003.1
291 */
pthread_mutex_destroy(pthread_mutex_t * mu)292 int pthread_mutex_destroy(pthread_mutex_t *mu)
293 {
294 int err;
295 size_t bit;
296 struct k_mutex *m;
297
298 m = get_posix_mutex(*mu);
299 if (m == NULL) {
300 return EINVAL;
301 }
302
303 bit = to_posix_mutex_idx(*mu);
304 err = sys_bitarray_free(&posix_mutex_bitarray, 1, bit);
305 __ASSERT_NO_MSG(err == 0);
306
307 LOG_DBG("Destroyed mutex %p", m);
308
309 return 0;
310 }
311
312 /**
313 * @brief Read protocol attribute for mutex.
314 *
315 * See IEEE 1003.1
316 */
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)317 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
318 int *protocol)
319 {
320 if ((attr == NULL) || (protocol == NULL)) {
321 return EINVAL;
322 }
323
324 *protocol = PTHREAD_PRIO_NONE;
325 return 0;
326 }
327
328 /**
329 * @brief Set protocol attribute for mutex.
330 *
331 * See IEEE 1003.1
332 */
pthread_mutexattr_setprotocol(pthread_mutexattr_t * attr,int protocol)333 int pthread_mutexattr_setprotocol(pthread_mutexattr_t *attr, int protocol)
334 {
335 if (attr == NULL) {
336 return EINVAL;
337 }
338
339 switch (protocol) {
340 case PTHREAD_PRIO_NONE:
341 return 0;
342 case PTHREAD_PRIO_INHERIT:
343 return ENOTSUP;
344 case PTHREAD_PRIO_PROTECT:
345 return ENOTSUP;
346 default:
347 return EINVAL;
348 }
349 }
350
pthread_mutexattr_init(pthread_mutexattr_t * attr)351 int pthread_mutexattr_init(pthread_mutexattr_t *attr)
352 {
353 struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
354
355 if (a == NULL) {
356 return EINVAL;
357 }
358
359 a->type = PTHREAD_MUTEX_DEFAULT;
360 a->initialized = true;
361
362 return 0;
363 }
364
pthread_mutexattr_destroy(pthread_mutexattr_t * attr)365 int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)
366 {
367 struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
368
369 if (a == NULL || !a->initialized) {
370 return EINVAL;
371 }
372
373 *a = (struct pthread_mutexattr){0};
374
375 return 0;
376 }
377
378 /**
379 * @brief Read type attribute for mutex.
380 *
381 * See IEEE 1003.1
382 */
pthread_mutexattr_gettype(const pthread_mutexattr_t * attr,int * type)383 int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type)
384 {
385 const struct pthread_mutexattr *a = (const struct pthread_mutexattr *)attr;
386
387 if (a == NULL || type == NULL || !a->initialized) {
388 return EINVAL;
389 }
390
391 *type = a->type;
392
393 return 0;
394 }
395
396 /**
397 * @brief Set type attribute for mutex.
398 *
399 * See IEEE 1003.1
400 */
pthread_mutexattr_settype(pthread_mutexattr_t * attr,int type)401 int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)
402 {
403 struct pthread_mutexattr *const a = (struct pthread_mutexattr *)attr;
404
405 if (a == NULL || !a->initialized) {
406 return EINVAL;
407 }
408
409 switch (type) {
410 case PTHREAD_MUTEX_NORMAL:
411 case PTHREAD_MUTEX_RECURSIVE:
412 case PTHREAD_MUTEX_ERRORCHECK:
413 a->type = type;
414 return 0;
415 default:
416 return EINVAL;
417 }
418 }
419
420 #ifdef CONFIG_POSIX_THREAD_PRIO_PROTECT
pthread_mutex_getprioceiling(const pthread_mutex_t * mutex,int * prioceiling)421 int pthread_mutex_getprioceiling(const pthread_mutex_t *mutex, int *prioceiling)
422 {
423 ARG_UNUSED(mutex);
424 ARG_UNUSED(prioceiling);
425
426 return ENOSYS;
427 }
428
pthread_mutex_setprioceiling(pthread_mutex_t * mutex,int prioceiling,int * old_ceiling)429 int pthread_mutex_setprioceiling(pthread_mutex_t *mutex, int prioceiling, int *old_ceiling)
430 {
431 ARG_UNUSED(mutex);
432 ARG_UNUSED(prioceiling);
433 ARG_UNUSED(old_ceiling);
434
435 return ENOSYS;
436 }
437
pthread_mutexattr_getprioceiling(const pthread_mutexattr_t * attr,int * prioceiling)438 int pthread_mutexattr_getprioceiling(const pthread_mutexattr_t *attr, int *prioceiling)
439 {
440 ARG_UNUSED(attr);
441 ARG_UNUSED(prioceiling);
442
443 return ENOSYS;
444 }
445
pthread_mutexattr_setprioceiling(pthread_mutexattr_t * attr,int prioceiling)446 int pthread_mutexattr_setprioceiling(pthread_mutexattr_t *attr, int prioceiling)
447 {
448 ARG_UNUSED(attr);
449 ARG_UNUSED(prioceiling);
450
451 return ENOSYS;
452 }
453
454 #endif /* CONFIG_POSIX_THREAD_PRIO_PROTECT */
455
456 __boot_func
pthread_mutex_pool_init(void)457 static int pthread_mutex_pool_init(void)
458 {
459 int err;
460 size_t i;
461
462 for (i = 0; i < CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
463 err = k_mutex_init(&posix_mutex_pool[i]);
464 __ASSERT_NO_MSG(err == 0);
465 }
466
467 return 0;
468 }
469 SYS_INIT(pthread_mutex_pool_init, PRE_KERNEL_1, 0);
470