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