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_mutex, CONFIG_PTHREAD_MUTEX_LOG_LEVEL);
17
18 static struct k_spinlock pthread_mutex_spinlock;
19
20 int64_t timespec_to_timeoutms(const struct timespec *abstime);
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 static struct k_mutex posix_mutex_pool[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
32 static uint8_t posix_mutex_type[CONFIG_MAX_PTHREAD_MUTEX_COUNT];
33 SYS_BITARRAY_DEFINE_STATIC(posix_mutex_bitarray, CONFIG_MAX_PTHREAD_MUTEX_COUNT);
34
35 /*
36 * We reserve the MSB to mark a pthread_mutex_t as initialized (from the
37 * perspective of the application). With a linear space, this means that
38 * the theoretical pthread_mutex_t range is [0,2147483647].
39 */
40 BUILD_ASSERT(CONFIG_MAX_PTHREAD_MUTEX_COUNT < PTHREAD_OBJ_MASK_INIT,
41 "CONFIG_MAX_PTHREAD_MUTEX_COUNT is too high");
42
posix_mutex_to_offset(struct k_mutex * m)43 static inline size_t posix_mutex_to_offset(struct k_mutex *m)
44 {
45 return m - posix_mutex_pool;
46 }
47
to_posix_mutex_idx(pthread_mutex_t mut)48 static inline size_t to_posix_mutex_idx(pthread_mutex_t mut)
49 {
50 return mark_pthread_obj_uninitialized(mut);
51 }
52
get_posix_mutex(pthread_mutex_t mu)53 static struct k_mutex *get_posix_mutex(pthread_mutex_t mu)
54 {
55 int actually_initialized;
56 size_t bit = to_posix_mutex_idx(mu);
57
58 /* if the provided mutex does not claim to be initialized, its invalid */
59 if (!is_pthread_obj_initialized(mu)) {
60 LOG_ERR("Mutex is uninitialized (%x)", mu);
61 return NULL;
62 }
63
64 /* Mask off the MSB to get the actual bit index */
65 if (sys_bitarray_test_bit(&posix_mutex_bitarray, bit, &actually_initialized) < 0) {
66 LOG_ERR("Mutex is invalid (%x)", mu);
67 return NULL;
68 }
69
70 if (actually_initialized == 0) {
71 /* The mutex claims to be initialized but is actually not */
72 LOG_ERR("Mutex claims to be initialized (%x)", mu);
73 return NULL;
74 }
75
76 return &posix_mutex_pool[bit];
77 }
78
to_posix_mutex(pthread_mutex_t * mu)79 struct k_mutex *to_posix_mutex(pthread_mutex_t *mu)
80 {
81 int err;
82 size_t bit;
83 struct k_mutex *m;
84
85 if (*mu != PTHREAD_MUTEX_INITIALIZER) {
86 return get_posix_mutex(*mu);
87 }
88
89 /* Try and automatically associate a posix_mutex */
90 if (sys_bitarray_alloc(&posix_mutex_bitarray, 1, &bit) < 0) {
91 LOG_ERR("Unable to allocate pthread_mutex_t");
92 return NULL;
93 }
94
95 /* Record the associated posix_mutex in mu and mark as initialized */
96 *mu = mark_pthread_obj_initialized(bit);
97
98 /* Initialize the posix_mutex */
99 m = &posix_mutex_pool[bit];
100
101 err = k_mutex_init(m);
102 __ASSERT_NO_MSG(err == 0);
103
104 return m;
105 }
106
acquire_mutex(pthread_mutex_t * mu,k_timeout_t timeout)107 static int acquire_mutex(pthread_mutex_t *mu, k_timeout_t timeout)
108 {
109 int type;
110 size_t bit;
111 int ret = 0;
112 struct k_mutex *m;
113 k_spinlock_key_t key;
114
115 key = k_spin_lock(&pthread_mutex_spinlock);
116
117 m = to_posix_mutex(mu);
118 if (m == NULL) {
119 k_spin_unlock(&pthread_mutex_spinlock, key);
120 return EINVAL;
121 }
122
123 LOG_DBG("Locking mutex %p with timeout %llx", m, timeout.ticks);
124
125 bit = posix_mutex_to_offset(m);
126 type = posix_mutex_type[bit];
127
128 if (m->owner == k_current_get()) {
129 switch (type) {
130 case PTHREAD_MUTEX_NORMAL:
131 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
132 k_spin_unlock(&pthread_mutex_spinlock, key);
133 LOG_ERR("Timeout locking mutex %p", m);
134 return EBUSY;
135 }
136 /* On most POSIX systems, this usually results in an infinite loop */
137 k_spin_unlock(&pthread_mutex_spinlock, key);
138 LOG_ERR("Attempt to relock non-recursive mutex %p", m);
139 do {
140 (void)k_sleep(K_FOREVER);
141 } while (true);
142 CODE_UNREACHABLE;
143 break;
144 case PTHREAD_MUTEX_RECURSIVE:
145 if (m->lock_count >= MUTEX_MAX_REC_LOCK) {
146 LOG_ERR("Mutex %p locked recursively too many times", m);
147 ret = EAGAIN;
148 }
149 break;
150 case PTHREAD_MUTEX_ERRORCHECK:
151 LOG_ERR("Attempt to recursively lock non-recursive mutex %p", m);
152 ret = EDEADLK;
153 break;
154 default:
155 __ASSERT(false, "invalid pthread type %d", type);
156 ret = EINVAL;
157 break;
158 }
159 }
160 k_spin_unlock(&pthread_mutex_spinlock, key);
161
162 if (ret == 0) {
163 ret = k_mutex_lock(m, timeout);
164 if (ret == -EAGAIN) {
165 LOG_ERR("Timeout locking mutex %p", m);
166 /*
167 * special quirk - k_mutex_lock() returns EAGAIN if a timeout occurs, but
168 * for pthreads, that means something different
169 */
170 ret = ETIMEDOUT;
171 }
172 }
173
174 if (ret < 0) {
175 LOG_ERR("k_mutex_unlock() failed: %d", ret);
176 ret = -ret;
177 }
178
179 if (ret == 0) {
180 LOG_DBG("Locked mutex %p", m);
181 }
182
183 return ret;
184 }
185
186 /**
187 * @brief Lock POSIX mutex with non-blocking call.
188 *
189 * See IEEE 1003.1
190 */
pthread_mutex_trylock(pthread_mutex_t * m)191 int pthread_mutex_trylock(pthread_mutex_t *m)
192 {
193 return acquire_mutex(m, K_NO_WAIT);
194 }
195
196 /**
197 * @brief Lock POSIX mutex with timeout.
198 *
199 *
200 * See IEEE 1003.1
201 */
pthread_mutex_timedlock(pthread_mutex_t * m,const struct timespec * abstime)202 int pthread_mutex_timedlock(pthread_mutex_t *m,
203 const struct timespec *abstime)
204 {
205 int32_t timeout = (int32_t)timespec_to_timeoutms(abstime);
206 return acquire_mutex(m, K_MSEC(timeout));
207 }
208
209 /**
210 * @brief Initialize POSIX mutex.
211 *
212 * See IEEE 1003.1
213 */
pthread_mutex_init(pthread_mutex_t * mu,const pthread_mutexattr_t * _attr)214 int pthread_mutex_init(pthread_mutex_t *mu, const pthread_mutexattr_t *_attr)
215 {
216 size_t bit;
217 struct k_mutex *m;
218 const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
219
220 *mu = PTHREAD_MUTEX_INITIALIZER;
221
222 m = to_posix_mutex(mu);
223 if (m == NULL) {
224 return ENOMEM;
225 }
226
227 bit = posix_mutex_to_offset(m);
228 if (attr == NULL) {
229 posix_mutex_type[bit] = def_attr.type;
230 } else {
231 posix_mutex_type[bit] = attr->type;
232 }
233
234 LOG_DBG("Initialized mutex %p", m);
235
236 return 0;
237 }
238
239
240 /**
241 * @brief Lock POSIX mutex with blocking call.
242 *
243 * See IEEE 1003.1
244 */
pthread_mutex_lock(pthread_mutex_t * m)245 int pthread_mutex_lock(pthread_mutex_t *m)
246 {
247 return acquire_mutex(m, K_FOREVER);
248 }
249
250 /**
251 * @brief Unlock POSIX mutex.
252 *
253 * See IEEE 1003.1
254 */
pthread_mutex_unlock(pthread_mutex_t * mu)255 int pthread_mutex_unlock(pthread_mutex_t *mu)
256 {
257 int ret;
258 struct k_mutex *m;
259
260 m = get_posix_mutex(*mu);
261 if (m == NULL) {
262 return EINVAL;
263 }
264
265 ret = k_mutex_unlock(m);
266 if (ret < 0) {
267 LOG_ERR("k_mutex_unlock() failed: %d", ret);
268 return -ret;
269 }
270
271 __ASSERT_NO_MSG(ret == 0);
272 LOG_DBG("Unlocked mutex %p", m);
273
274 return 0;
275 }
276
277 /**
278 * @brief Destroy POSIX mutex.
279 *
280 * See IEEE 1003.1
281 */
pthread_mutex_destroy(pthread_mutex_t * mu)282 int pthread_mutex_destroy(pthread_mutex_t *mu)
283 {
284 int err;
285 size_t bit;
286 struct k_mutex *m;
287
288 m = get_posix_mutex(*mu);
289 if (m == NULL) {
290 return EINVAL;
291 }
292
293 bit = to_posix_mutex_idx(*mu);
294 err = sys_bitarray_free(&posix_mutex_bitarray, 1, bit);
295 __ASSERT_NO_MSG(err == 0);
296
297 LOG_DBG("Destroyed mutex %p", m);
298
299 return 0;
300 }
301
302 /**
303 * @brief Read protocol attribute for mutex.
304 *
305 * See IEEE 1003.1
306 */
pthread_mutexattr_getprotocol(const pthread_mutexattr_t * attr,int * protocol)307 int pthread_mutexattr_getprotocol(const pthread_mutexattr_t *attr,
308 int *protocol)
309 {
310 *protocol = PTHREAD_PRIO_NONE;
311 return 0;
312 }
313
314 /**
315 * @brief Read type attribute for mutex.
316 *
317 * See IEEE 1003.1
318 */
pthread_mutexattr_gettype(const pthread_mutexattr_t * _attr,int * type)319 int pthread_mutexattr_gettype(const pthread_mutexattr_t *_attr, int *type)
320 {
321 const struct pthread_mutexattr *attr = (const struct pthread_mutexattr *)_attr;
322
323 *type = attr->type;
324 return 0;
325 }
326
327 /**
328 * @brief Set type attribute for mutex.
329 *
330 * See IEEE 1003.1
331 */
pthread_mutexattr_settype(pthread_mutexattr_t * _attr,int type)332 int pthread_mutexattr_settype(pthread_mutexattr_t *_attr, int type)
333 {
334 struct pthread_mutexattr *attr = (struct pthread_mutexattr *)_attr;
335 int retc = EINVAL;
336
337 if ((type == PTHREAD_MUTEX_NORMAL) ||
338 (type == PTHREAD_MUTEX_RECURSIVE) ||
339 (type == PTHREAD_MUTEX_ERRORCHECK)) {
340 attr->type = type;
341 retc = 0;
342 }
343
344 return retc;
345 }
346
pthread_mutex_pool_init(void)347 static int pthread_mutex_pool_init(void)
348 {
349 int err;
350 size_t i;
351
352 for (i = 0; i < CONFIG_MAX_PTHREAD_MUTEX_COUNT; ++i) {
353 err = k_mutex_init(&posix_mutex_pool[i]);
354 __ASSERT_NO_MSG(err == 0);
355 }
356
357 return 0;
358 }
359 SYS_INIT(pthread_mutex_pool_init, PRE_KERNEL_1, 0);
360