blob: 890f257691c650c7bc8a0353337957ef38cdfff4 [file] [log] [blame]
/*
* 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;
}