1 /*
2  * Copyright (c) 2018 Intel Corporation
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include "posix_clock.h"
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 #define CONCURRENT_READER_LIMIT  (CONFIG_POSIX_THREAD_THREADS_MAX + 1)
18 
19 struct posix_rwlock {
20 	struct sys_sem rd_sem;
21 	struct sys_sem wr_sem;
22 	struct sys_sem reader_active; /* blocks WR till reader has acquired lock */
23 	k_tid_t wr_owner;
24 };
25 
26 struct posix_rwlockattr {
27 	bool initialized: 1;
28 	bool pshared: 1;
29 };
30 
31 static uint32_t read_lock_acquire(struct posix_rwlock *rwl, uint32_t timeout);
32 static uint32_t write_lock_acquire(struct posix_rwlock *rwl, uint32_t timeout);
33 
34 LOG_MODULE_REGISTER(pthread_rwlock, CONFIG_PTHREAD_RWLOCK_LOG_LEVEL);
35 
36 static SYS_SEM_DEFINE(posix_rwlock_lock, 1, 1);
37 
38 static struct posix_rwlock posix_rwlock_pool[CONFIG_MAX_PTHREAD_RWLOCK_COUNT];
39 SYS_BITARRAY_DEFINE_STATIC(posix_rwlock_bitarray, CONFIG_MAX_PTHREAD_RWLOCK_COUNT);
40 
41 /*
42  * We reserve the MSB to mark a pthread_rwlock_t as initialized (from the
43  * perspective of the application). With a linear space, this means that
44  * the theoretical pthread_rwlock_t range is [0,2147483647].
45  */
46 BUILD_ASSERT(CONFIG_MAX_PTHREAD_RWLOCK_COUNT < PTHREAD_OBJ_MASK_INIT,
47 	     "CONFIG_MAX_PTHREAD_RWLOCK_COUNT is too high");
48 
posix_rwlock_to_offset(struct posix_rwlock * rwl)49 static inline size_t posix_rwlock_to_offset(struct posix_rwlock *rwl)
50 {
51 	return rwl - posix_rwlock_pool;
52 }
53 
to_posix_rwlock_idx(pthread_rwlock_t rwlock)54 static inline size_t to_posix_rwlock_idx(pthread_rwlock_t rwlock)
55 {
56 	return mark_pthread_obj_uninitialized(rwlock);
57 }
58 
get_posix_rwlock(pthread_rwlock_t rwlock)59 static struct posix_rwlock *get_posix_rwlock(pthread_rwlock_t rwlock)
60 {
61 	int actually_initialized;
62 	size_t bit = to_posix_rwlock_idx(rwlock);
63 
64 	/* if the provided rwlock does not claim to be initialized, its invalid */
65 	if (!is_pthread_obj_initialized(rwlock)) {
66 		LOG_DBG("RWlock is uninitialized (%x)", rwlock);
67 		return NULL;
68 	}
69 
70 	/* Mask off the MSB to get the actual bit index */
71 	if (sys_bitarray_test_bit(&posix_rwlock_bitarray, bit, &actually_initialized) < 0) {
72 		LOG_DBG("RWlock is invalid (%x)", rwlock);
73 		return NULL;
74 	}
75 
76 	if (actually_initialized == 0) {
77 		/* The rwlock claims to be initialized but is actually not */
78 		LOG_DBG("RWlock claims to be initialized (%x)", rwlock);
79 		return NULL;
80 	}
81 
82 	return &posix_rwlock_pool[bit];
83 }
84 
to_posix_rwlock(pthread_rwlock_t * rwlock)85 struct posix_rwlock *to_posix_rwlock(pthread_rwlock_t *rwlock)
86 {
87 	size_t bit;
88 	struct posix_rwlock *rwl;
89 
90 	if (*rwlock != PTHREAD_RWLOCK_INITIALIZER) {
91 		return get_posix_rwlock(*rwlock);
92 	}
93 
94 	/* Try and automatically associate a posix_rwlock */
95 	if (sys_bitarray_alloc(&posix_rwlock_bitarray, 1, &bit) < 0) {
96 		LOG_DBG("Unable to allocate pthread_rwlock_t");
97 		return NULL;
98 	}
99 
100 	/* Record the associated posix_rwlock in rwl and mark as initialized */
101 	*rwlock = mark_pthread_obj_initialized(bit);
102 
103 	/* Initialize the posix_rwlock */
104 	rwl = &posix_rwlock_pool[bit];
105 
106 	return rwl;
107 }
108 
109 /**
110  * @brief Initialize read-write lock object.
111  *
112  * See IEEE 1003.1
113  */
pthread_rwlock_init(pthread_rwlock_t * rwlock,const pthread_rwlockattr_t * attr)114 int pthread_rwlock_init(pthread_rwlock_t *rwlock,
115 			const pthread_rwlockattr_t *attr)
116 {
117 	struct posix_rwlock *rwl;
118 
119 	ARG_UNUSED(attr);
120 	*rwlock = PTHREAD_RWLOCK_INITIALIZER;
121 
122 	rwl = to_posix_rwlock(rwlock);
123 	if (rwl == NULL) {
124 		return ENOMEM;
125 	}
126 
127 	sys_sem_init(&rwl->rd_sem, CONCURRENT_READER_LIMIT, CONCURRENT_READER_LIMIT);
128 	sys_sem_init(&rwl->wr_sem, 1, 1);
129 	sys_sem_init(&rwl->reader_active, 1, 1);
130 	rwl->wr_owner = NULL;
131 
132 	LOG_DBG("Initialized rwlock %p", rwl);
133 
134 	return 0;
135 }
136 
137 /**
138  * @brief Destroy read-write lock object.
139  *
140  * See IEEE 1003.1
141  */
pthread_rwlock_destroy(pthread_rwlock_t * rwlock)142 int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)
143 {
144 	int err;
145 	size_t bit;
146 	int ret = EINVAL;
147 	struct posix_rwlock *rwl;
148 
149 	SYS_SEM_LOCK(&posix_rwlock_lock) {
150 		rwl = get_posix_rwlock(*rwlock);
151 		if (rwl == NULL) {
152 			ret = EINVAL;
153 			SYS_SEM_LOCK_BREAK;
154 		}
155 
156 		if (rwl->wr_owner != NULL) {
157 			ret = EBUSY;
158 			SYS_SEM_LOCK_BREAK;
159 		}
160 
161 		ret = 0;
162 		bit = posix_rwlock_to_offset(rwl);
163 		err = sys_bitarray_free(&posix_rwlock_bitarray, 1, bit);
164 		__ASSERT_NO_MSG(err == 0);
165 	}
166 
167 	return ret;
168 }
169 
170 /**
171  * @brief Lock a read-write lock object for reading.
172  *
173  * API behaviour is unpredictable if number of concurrent reader
174  * lock held is greater than CONCURRENT_READER_LIMIT.
175  *
176  * See IEEE 1003.1
177  */
pthread_rwlock_rdlock(pthread_rwlock_t * rwlock)178 int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)
179 {
180 	struct posix_rwlock *rwl;
181 
182 	rwl = get_posix_rwlock(*rwlock);
183 	if (rwl == NULL) {
184 		return EINVAL;
185 	}
186 
187 	return read_lock_acquire(rwl, SYS_FOREVER_MS);
188 }
189 
190 /**
191  * @brief Lock a read-write lock object for reading within specific time.
192  *
193  * API behaviour is unpredictable if number of concurrent reader
194  * lock held is greater than CONCURRENT_READER_LIMIT.
195  *
196  * See IEEE 1003.1
197  */
pthread_rwlock_timedrdlock(pthread_rwlock_t * rwlock,const struct timespec * abstime)198 int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock,
199 			       const struct timespec *abstime)
200 {
201 	uint32_t ret = 0U;
202 	struct posix_rwlock *rwl;
203 
204 	if ((abstime == NULL) || !timespec_is_valid(abstime)) {
205 		LOG_DBG("%s is invalid", "abstime");
206 		return EINVAL;
207 	}
208 
209 	rwl = get_posix_rwlock(*rwlock);
210 	if (rwl == NULL) {
211 		return EINVAL;
212 	}
213 
214 	if (read_lock_acquire(rwl, timespec_to_timeoutms(CLOCK_REALTIME, abstime)) != 0U) {
215 		ret = ETIMEDOUT;
216 	}
217 
218 	return ret;
219 }
220 
221 /**
222  * @brief Lock a read-write lock object for reading immediately.
223  *
224  * API behaviour is unpredictable if number of concurrent reader
225  * lock held is greater than CONCURRENT_READER_LIMIT.
226  *
227  * See IEEE 1003.1
228  */
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)229 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
230 {
231 	struct posix_rwlock *rwl;
232 
233 	rwl = get_posix_rwlock(*rwlock);
234 	if (rwl == NULL) {
235 		return EINVAL;
236 	}
237 
238 	return read_lock_acquire(rwl, 0);
239 }
240 
241 /**
242  * @brief Lock a read-write lock object for writing.
243  *
244  * Write lock does not have priority over reader lock,
245  * threads get lock based on priority.
246  *
247  * See IEEE 1003.1
248  */
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)249 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
250 {
251 	struct posix_rwlock *rwl;
252 
253 	rwl = get_posix_rwlock(*rwlock);
254 	if (rwl == NULL) {
255 		return EINVAL;
256 	}
257 
258 	return write_lock_acquire(rwl, SYS_FOREVER_MS);
259 }
260 
261 /**
262  * @brief Lock a read-write lock object for writing within specific time.
263  *
264  * Write lock does not have priority over reader lock,
265  * threads get lock based on priority.
266  *
267  * See IEEE 1003.1
268  */
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlock,const struct timespec * abstime)269 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
270 			       const struct timespec *abstime)
271 {
272 	uint32_t ret = 0U;
273 	struct posix_rwlock *rwl;
274 
275 	if ((abstime == NULL) || !timespec_is_valid(abstime)) {
276 		LOG_DBG("%s is invalid", "abstime");
277 		return EINVAL;
278 	}
279 
280 	rwl = get_posix_rwlock(*rwlock);
281 	if (rwl == NULL) {
282 		return EINVAL;
283 	}
284 
285 	if (write_lock_acquire(rwl, timespec_to_timeoutms(CLOCK_REALTIME, abstime)) != 0U) {
286 		ret = ETIMEDOUT;
287 	}
288 
289 	return ret;
290 }
291 
292 /**
293  * @brief Lock a read-write lock object for writing immediately.
294  *
295  * Write lock does not have priority over reader lock,
296  * threads get lock based on priority.
297  *
298  * See IEEE 1003.1
299  */
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)300 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
301 {
302 	struct posix_rwlock *rwl;
303 
304 	rwl = get_posix_rwlock(*rwlock);
305 	if (rwl == NULL) {
306 		return EINVAL;
307 	}
308 
309 	return write_lock_acquire(rwl, 0);
310 }
311 
312 /**
313  *
314  * @brief Unlock a read-write lock object.
315  *
316  * See IEEE 1003.1
317  */
pthread_rwlock_unlock(pthread_rwlock_t * rwlock)318 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
319 {
320 	struct posix_rwlock *rwl;
321 
322 	rwl = get_posix_rwlock(*rwlock);
323 	if (rwl == NULL) {
324 		return EINVAL;
325 	}
326 
327 	if (k_current_get() == rwl->wr_owner) {
328 		/* Write unlock */
329 		rwl->wr_owner = NULL;
330 		(void)sys_sem_give(&rwl->reader_active);
331 		(void)sys_sem_give(&rwl->wr_sem);
332 	} else {
333 		/* Read unlock */
334 		(void)sys_sem_give(&rwl->rd_sem);
335 
336 		if (sys_sem_count_get(&rwl->rd_sem) == CONCURRENT_READER_LIMIT) {
337 			/* Last read lock, unlock writer */
338 			(void)sys_sem_give(&rwl->reader_active);
339 		}
340 	}
341 	return 0;
342 }
343 
read_lock_acquire(struct posix_rwlock * rwl,uint32_t timeout)344 static uint32_t read_lock_acquire(struct posix_rwlock *rwl, uint32_t timeout)
345 {
346 	uint32_t ret = 0U;
347 
348 	if (sys_sem_take(&rwl->wr_sem, SYS_TIMEOUT_MS(timeout)) == 0) {
349 		(void)sys_sem_take(&rwl->reader_active, K_NO_WAIT);
350 		(void)sys_sem_take(&rwl->rd_sem, K_NO_WAIT);
351 		(void)sys_sem_give(&rwl->wr_sem);
352 	} else {
353 		ret = EBUSY;
354 	}
355 
356 	return ret;
357 }
358 
write_lock_acquire(struct posix_rwlock * rwl,uint32_t timeout)359 static uint32_t write_lock_acquire(struct posix_rwlock *rwl, uint32_t timeout)
360 {
361 	uint32_t ret = 0U;
362 	int64_t elapsed_time, st_time = k_uptime_get();
363 	k_timeout_t k_timeout;
364 
365 	k_timeout = SYS_TIMEOUT_MS(timeout);
366 
367 	/* waiting for release of write lock */
368 	if (sys_sem_take(&rwl->wr_sem, k_timeout) == 0) {
369 		/* update remaining timeout time for 2nd sem */
370 		if (timeout != SYS_FOREVER_MS) {
371 			elapsed_time = k_uptime_get() - st_time;
372 			timeout = timeout <= elapsed_time ? 0 :
373 				  timeout - elapsed_time;
374 		}
375 
376 		k_timeout = SYS_TIMEOUT_MS(timeout);
377 
378 		/* waiting for reader to complete operation */
379 		if (sys_sem_take(&rwl->reader_active, k_timeout) == 0) {
380 			rwl->wr_owner = k_current_get();
381 		} else {
382 			(void)sys_sem_give(&rwl->wr_sem);
383 			ret = EBUSY;
384 		}
385 
386 	} else {
387 		ret = EBUSY;
388 	}
389 	return ret;
390 }
391 
pthread_rwlockattr_getpshared(const pthread_rwlockattr_t * ZRESTRICT attr,int * ZRESTRICT pshared)392 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *ZRESTRICT attr,
393 				  int *ZRESTRICT pshared)
394 {
395 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
396 
397 	if (a == NULL || !a->initialized) {
398 		return EINVAL;
399 	}
400 
401 	*pshared = a->pshared;
402 
403 	return 0;
404 }
405 
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * attr,int pshared)406 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
407 {
408 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
409 
410 	if (a == NULL || !a->initialized) {
411 		return EINVAL;
412 	}
413 
414 	if (!(pshared == PTHREAD_PROCESS_PRIVATE || pshared == PTHREAD_PROCESS_SHARED)) {
415 		return EINVAL;
416 	}
417 
418 	a->pshared = pshared;
419 
420 	return 0;
421 }
422 
pthread_rwlockattr_init(pthread_rwlockattr_t * attr)423 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
424 {
425 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
426 
427 	if (a == NULL) {
428 		return EINVAL;
429 	}
430 
431 	*a = (struct posix_rwlockattr){
432 		.initialized = true,
433 		.pshared = PTHREAD_PROCESS_PRIVATE,
434 	};
435 
436 	return 0;
437 }
438 
pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr)439 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
440 {
441 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
442 
443 	if (a == NULL || !a->initialized) {
444 		return EINVAL;
445 	}
446 
447 	*a = (struct posix_rwlockattr){0};
448 
449 	return 0;
450 }
451