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