blob: 9639444d76542455364b74a08de864d664ed696e [file] [log] [blame]
/*
* Copyright (c) 2018 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <kernel.h>
#include <errno.h>
#include <posix/time.h>
#include <posix/posix_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 = 0U;
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) != 0U) {
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 = 0U;
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) != 0U) {
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 = 0U;
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 = 0U;
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;
}