1 /*
2  * Copyright (c) 2018 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/logging/log.h>
12 #include <zephyr/posix/pthread.h>
13 #include <zephyr/sys/bitarray.h>
14 #include <zephyr/sys/sem.h>
15 
16 #define CONCURRENT_READER_LIMIT  (CONFIG_POSIX_THREAD_THREADS_MAX + 1)
17 
18 struct posix_rwlock {
19 	struct sys_sem rd_sem;
20 	struct sys_sem wr_sem;
21 	struct sys_sem reader_active; /* blocks WR till reader has acquired lock */
22 	k_tid_t wr_owner;
23 };
24 
25 struct posix_rwlockattr {
26 	bool initialized: 1;
27 	bool pshared: 1;
28 };
29 
30 int64_t timespec_to_timeoutms(const struct timespec *abstime);
31 static uint32_t read_lock_acquire(struct posix_rwlock *rwl, int32_t timeout);
32 static uint32_t write_lock_acquire(struct posix_rwlock *rwl, int32_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 	int32_t timeout;
202 	uint32_t ret = 0U;
203 	struct posix_rwlock *rwl;
204 
205 	if (abstime->tv_nsec < 0 || abstime->tv_nsec > NSEC_PER_SEC) {
206 		return EINVAL;
207 	}
208 
209 	timeout = (int32_t) timespec_to_timeoutms(abstime);
210 
211 	rwl = get_posix_rwlock(*rwlock);
212 	if (rwl == NULL) {
213 		return EINVAL;
214 	}
215 
216 	if (read_lock_acquire(rwl, timeout) != 0U) {
217 		ret = ETIMEDOUT;
218 	}
219 
220 	return ret;
221 }
222 
223 /**
224  * @brief Lock a read-write lock object for reading immediately.
225  *
226  * API behaviour is unpredictable if number of concurrent reader
227  * lock held is greater than CONCURRENT_READER_LIMIT.
228  *
229  * See IEEE 1003.1
230  */
pthread_rwlock_tryrdlock(pthread_rwlock_t * rwlock)231 int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)
232 {
233 	struct posix_rwlock *rwl;
234 
235 	rwl = get_posix_rwlock(*rwlock);
236 	if (rwl == NULL) {
237 		return EINVAL;
238 	}
239 
240 	return read_lock_acquire(rwl, 0);
241 }
242 
243 /**
244  * @brief Lock a read-write lock object for writing.
245  *
246  * Write lock does not have priority over reader lock,
247  * threads get lock based on priority.
248  *
249  * See IEEE 1003.1
250  */
pthread_rwlock_wrlock(pthread_rwlock_t * rwlock)251 int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)
252 {
253 	struct posix_rwlock *rwl;
254 
255 	rwl = get_posix_rwlock(*rwlock);
256 	if (rwl == NULL) {
257 		return EINVAL;
258 	}
259 
260 	return write_lock_acquire(rwl, SYS_FOREVER_MS);
261 }
262 
263 /**
264  * @brief Lock a read-write lock object for writing within specific time.
265  *
266  * Write lock does not have priority over reader lock,
267  * threads get lock based on priority.
268  *
269  * See IEEE 1003.1
270  */
pthread_rwlock_timedwrlock(pthread_rwlock_t * rwlock,const struct timespec * abstime)271 int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock,
272 			       const struct timespec *abstime)
273 {
274 	int32_t timeout;
275 	uint32_t ret = 0U;
276 	struct posix_rwlock *rwl;
277 
278 	if (abstime->tv_nsec < 0 || abstime->tv_nsec > NSEC_PER_SEC) {
279 		return EINVAL;
280 	}
281 
282 	timeout = (int32_t) timespec_to_timeoutms(abstime);
283 
284 	rwl = get_posix_rwlock(*rwlock);
285 	if (rwl == NULL) {
286 		return EINVAL;
287 	}
288 
289 	if (write_lock_acquire(rwl, timeout) != 0U) {
290 		ret = ETIMEDOUT;
291 	}
292 
293 	return ret;
294 }
295 
296 /**
297  * @brief Lock a read-write lock object for writing immediately.
298  *
299  * Write lock does not have priority over reader lock,
300  * threads get lock based on priority.
301  *
302  * See IEEE 1003.1
303  */
pthread_rwlock_trywrlock(pthread_rwlock_t * rwlock)304 int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)
305 {
306 	struct posix_rwlock *rwl;
307 
308 	rwl = get_posix_rwlock(*rwlock);
309 	if (rwl == NULL) {
310 		return EINVAL;
311 	}
312 
313 	return write_lock_acquire(rwl, 0);
314 }
315 
316 /**
317  *
318  * @brief Unlock a read-write lock object.
319  *
320  * See IEEE 1003.1
321  */
pthread_rwlock_unlock(pthread_rwlock_t * rwlock)322 int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)
323 {
324 	struct posix_rwlock *rwl;
325 
326 	rwl = get_posix_rwlock(*rwlock);
327 	if (rwl == NULL) {
328 		return EINVAL;
329 	}
330 
331 	if (k_current_get() == rwl->wr_owner) {
332 		/* Write unlock */
333 		rwl->wr_owner = NULL;
334 		(void)sys_sem_give(&rwl->reader_active);
335 		(void)sys_sem_give(&rwl->wr_sem);
336 	} else {
337 		/* Read unlock */
338 		(void)sys_sem_give(&rwl->rd_sem);
339 
340 		if (sys_sem_count_get(&rwl->rd_sem) == CONCURRENT_READER_LIMIT) {
341 			/* Last read lock, unlock writer */
342 			(void)sys_sem_give(&rwl->reader_active);
343 		}
344 	}
345 	return 0;
346 }
347 
read_lock_acquire(struct posix_rwlock * rwl,int32_t timeout)348 static uint32_t read_lock_acquire(struct posix_rwlock *rwl, int32_t timeout)
349 {
350 	uint32_t ret = 0U;
351 
352 	if (sys_sem_take(&rwl->wr_sem, SYS_TIMEOUT_MS(timeout)) == 0) {
353 		(void)sys_sem_take(&rwl->reader_active, K_NO_WAIT);
354 		(void)sys_sem_take(&rwl->rd_sem, K_NO_WAIT);
355 		(void)sys_sem_give(&rwl->wr_sem);
356 	} else {
357 		ret = EBUSY;
358 	}
359 
360 	return ret;
361 }
362 
write_lock_acquire(struct posix_rwlock * rwl,int32_t timeout)363 static uint32_t write_lock_acquire(struct posix_rwlock *rwl, int32_t timeout)
364 {
365 	uint32_t ret = 0U;
366 	int64_t elapsed_time, st_time = k_uptime_get();
367 	k_timeout_t k_timeout;
368 
369 	k_timeout = SYS_TIMEOUT_MS(timeout);
370 
371 	/* waiting for release of write lock */
372 	if (sys_sem_take(&rwl->wr_sem, k_timeout) == 0) {
373 		/* update remaining timeout time for 2nd sem */
374 		if (timeout != SYS_FOREVER_MS) {
375 			elapsed_time = k_uptime_get() - st_time;
376 			timeout = timeout <= elapsed_time ? 0 :
377 				  timeout - elapsed_time;
378 		}
379 
380 		k_timeout = SYS_TIMEOUT_MS(timeout);
381 
382 		/* waiting for reader to complete operation */
383 		if (sys_sem_take(&rwl->reader_active, k_timeout) == 0) {
384 			rwl->wr_owner = k_current_get();
385 		} else {
386 			(void)sys_sem_give(&rwl->wr_sem);
387 			ret = EBUSY;
388 		}
389 
390 	} else {
391 		ret = EBUSY;
392 	}
393 	return ret;
394 }
395 
pthread_rwlockattr_getpshared(const pthread_rwlockattr_t * ZRESTRICT attr,int * ZRESTRICT pshared)396 int pthread_rwlockattr_getpshared(const pthread_rwlockattr_t *ZRESTRICT attr,
397 				  int *ZRESTRICT pshared)
398 {
399 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
400 
401 	if (a == NULL || !a->initialized) {
402 		return EINVAL;
403 	}
404 
405 	*pshared = a->pshared;
406 
407 	return 0;
408 }
409 
pthread_rwlockattr_setpshared(pthread_rwlockattr_t * attr,int pshared)410 int pthread_rwlockattr_setpshared(pthread_rwlockattr_t *attr, int pshared)
411 {
412 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
413 
414 	if (a == NULL || !a->initialized) {
415 		return EINVAL;
416 	}
417 
418 	if (!(pshared == PTHREAD_PROCESS_PRIVATE || pshared == PTHREAD_PROCESS_SHARED)) {
419 		return EINVAL;
420 	}
421 
422 	a->pshared = pshared;
423 
424 	return 0;
425 }
426 
pthread_rwlockattr_init(pthread_rwlockattr_t * attr)427 int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)
428 {
429 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
430 
431 	if (a == NULL) {
432 		return EINVAL;
433 	}
434 
435 	*a = (struct posix_rwlockattr){
436 		.initialized = true,
437 		.pshared = PTHREAD_PROCESS_PRIVATE,
438 	};
439 
440 	return 0;
441 }
442 
pthread_rwlockattr_destroy(pthread_rwlockattr_t * attr)443 int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)
444 {
445 	struct posix_rwlockattr *const a = (struct posix_rwlockattr *)attr;
446 
447 	if (a == NULL || !a->initialized) {
448 		return EINVAL;
449 	}
450 
451 	*a = (struct posix_rwlockattr){0};
452 
453 	return 0;
454 }
455