| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include <kernel.h> |
| #include <time.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| |
| #define INITIALIZED 1 |
| #define NOT_INITIALIZED 0 |
| |
| #define CONCURRENT_READER_LIMIT (CONFIG_MAX_PTHREAD_COUNT + 1) |
| |
| static s64_t calculate_timeout(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) calculate_timeout(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) calculate_timeout(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; |
| } |
| |
| static s64_t calculate_timeout(const struct timespec *abstime) |
| { |
| s64_t milli_secs; |
| s32_t secs; |
| struct timespec curtime; |
| |
| /* FIXME: Zephyr does have CLOCK_REALTIME to get time. |
| * As per POSIX standard time should be calculated wrt CLOCK_REALTIME. |
| * Zephyr deviates from POSIX 1003.1 standard on this aspect. |
| */ |
| clock_gettime(CLOCK_MONOTONIC, &curtime); |
| secs = abstime->tv_sec - curtime.tv_sec; |
| |
| if (abstime->tv_sec < curtime.tv_sec || |
| (secs == 0 && abstime->tv_nsec <= curtime.tv_nsec)) { |
| milli_secs = K_NO_WAIT; |
| } else { |
| milli_secs = (abstime->tv_nsec - curtime.tv_nsec) / |
| NSEC_PER_MSEC; |
| |
| if (milli_secs < 0) { |
| milli_secs += MSEC_PER_SEC; |
| secs -= 1; |
| } |
| |
| milli_secs += (long)secs * MSEC_PER_SEC; |
| } |
| |
| return milli_secs; |
| } |
| |