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