| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <kernel.h> |
| #include <errno.h> |
| #include <posix/time.h> |
| #include <posix/sys/types.h> |
| |
| #define INITIALIZED 1 |
| #define NOT_INITIALIZED 0 |
| |
| #define CONCURRENT_READER_LIMIT (CONFIG_MAX_PTHREAD_COUNT + 1) |
| |
| s64_t timespec_to_timeoutms(const struct timespec *abstime); |
| static u32_t read_lock_acquire(pthread_rwlock_t *rwlock, s32_t timeout); |
| static u32_t write_lock_acquire(pthread_rwlock_t *rwlock, s32_t timeout); |
| |
| /** |
| * @brief Initialize read-write lock object. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_init(pthread_rwlock_t *rwlock, |
| const pthread_rwlockattr_t *attr) |
| { |
| k_sem_init(&rwlock->rd_sem, CONCURRENT_READER_LIMIT, |
| CONCURRENT_READER_LIMIT); |
| k_sem_init(&rwlock->wr_sem, 1, 1); |
| k_sem_init(&rwlock->reader_active, 1, 1); |
| rwlock->wr_owner = NULL; |
| rwlock->status = INITIALIZED; |
| return 0; |
| } |
| |
| /** |
| * @brief Destroy read-write lock object. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_destroy(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| if (rwlock->wr_owner != NULL) { |
| return EBUSY; |
| } |
| |
| if (rwlock->status == INITIALIZED) { |
| rwlock->status = NOT_INITIALIZED; |
| return 0; |
| } |
| |
| return EINVAL; |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for reading. |
| * |
| * API behaviour is unpredictable if number of concurrent reader |
| * lock held is greater than CONCURRENT_READER_LIMIT. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| return read_lock_acquire(rwlock, K_FOREVER); |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for reading within specific time. |
| * |
| * API behaviour is unpredictable if number of concurrent reader |
| * lock held is greater than CONCURRENT_READER_LIMIT. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_timedrdlock(pthread_rwlock_t *rwlock, |
| const struct timespec *abstime) |
| { |
| s32_t timeout; |
| u32_t ret = 0; |
| |
| if (rwlock->status == NOT_INITIALIZED || abstime->tv_nsec < 0 || |
| abstime->tv_nsec > NSEC_PER_SEC) { |
| return EINVAL; |
| } |
| |
| timeout = (s32_t) timespec_to_timeoutms(abstime); |
| |
| if (read_lock_acquire(rwlock, timeout) != 0) { |
| ret = ETIMEDOUT; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for reading immedately. |
| * |
| * API behaviour is unpredictable if number of concurrent reader |
| * lock held is greater than CONCURRENT_READER_LIMIT. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| return read_lock_acquire(rwlock, K_NO_WAIT); |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for writing. |
| * |
| * Write lock does not have priority over reader lock, |
| * threads get lock based on priority. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| return write_lock_acquire(rwlock, K_FOREVER); |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for writing within specific time. |
| * |
| * Write lock does not have priority over reader lock, |
| * threads get lock based on priority. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock, |
| const struct timespec *abstime) |
| { |
| s32_t timeout; |
| u32_t ret = 0; |
| |
| if (rwlock->status == NOT_INITIALIZED || abstime->tv_nsec < 0 || |
| abstime->tv_nsec > NSEC_PER_SEC) { |
| return EINVAL; |
| } |
| |
| timeout = (s32_t) timespec_to_timeoutms(abstime); |
| |
| if (write_lock_acquire(rwlock, timeout) != 0) { |
| ret = ETIMEDOUT; |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * @brief Lock a read-write lock object for writing immedately. |
| * |
| * Write lock does not have priority over reader lock, |
| * threads get lock based on priority. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| return write_lock_acquire(rwlock, K_NO_WAIT); |
| } |
| |
| /** |
| * |
| * @brief Unlock a read-write lock object. |
| * |
| * See IEEE 1003.1 |
| */ |
| int pthread_rwlock_unlock(pthread_rwlock_t *rwlock) |
| { |
| if (rwlock->status == NOT_INITIALIZED) { |
| return EINVAL; |
| } |
| |
| if (k_current_get() == rwlock->wr_owner) { |
| /* Write unlock */ |
| rwlock->wr_owner = NULL; |
| k_sem_give(&rwlock->reader_active); |
| k_sem_give(&rwlock->wr_sem); |
| } else { |
| /* Read unlock */ |
| if (k_sem_count_get(&rwlock->rd_sem) == |
| (CONCURRENT_READER_LIMIT - 1)) { |
| /* Last read lock, unlock writer */ |
| k_sem_give(&rwlock->reader_active); |
| } |
| |
| k_sem_give(&rwlock->rd_sem); |
| } |
| return 0; |
| } |
| |
| |
| static u32_t read_lock_acquire(pthread_rwlock_t *rwlock, s32_t timeout) |
| { |
| u32_t ret = 0; |
| |
| if (k_sem_take(&rwlock->wr_sem, timeout) == 0) { |
| k_sem_take(&rwlock->reader_active, K_NO_WAIT); |
| k_sem_take(&rwlock->rd_sem, K_NO_WAIT); |
| k_sem_give(&rwlock->wr_sem); |
| } else { |
| ret = EBUSY; |
| } |
| |
| return ret; |
| } |
| |
| static u32_t write_lock_acquire(pthread_rwlock_t *rwlock, s32_t timeout) |
| { |
| u32_t ret = 0; |
| s64_t elapsed_time, st_time = k_uptime_get(); |
| |
| /* waiting for release of write lock */ |
| if (k_sem_take(&rwlock->wr_sem, timeout) == 0) { |
| if (timeout > K_NO_WAIT) { |
| elapsed_time = k_uptime_get() - st_time; |
| timeout = timeout <= elapsed_time ? K_NO_WAIT : |
| timeout - elapsed_time; |
| } |
| |
| /* waiting for reader to complete operation */ |
| if (k_sem_take(&rwlock->reader_active, timeout) == 0) { |
| rwlock->wr_owner = k_current_get(); |
| } else { |
| k_sem_give(&rwlock->wr_sem); |
| ret = EBUSY; |
| } |
| |
| } else { |
| ret = EBUSY; |
| } |
| return ret; |
| } |
| |
| |