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