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