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