389 lines
6.8 KiB
C
389 lines
6.8 KiB
C
/*
|
|
* Copyright (c) 2018 Intel Corporation
|
|
* Copyright (c) 2023 Meta
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/sys/atomic.h>
|
|
#include <zephyr/posix/fcntl.h>
|
|
#include <zephyr/posix/pthread.h>
|
|
#include <zephyr/posix/semaphore.h>
|
|
|
|
struct nsem_obj {
|
|
sys_snode_t snode;
|
|
sem_t sem;
|
|
int ref_count;
|
|
char *name;
|
|
};
|
|
|
|
/* Initialize the list */
|
|
static sys_slist_t nsem_list = SYS_SLIST_STATIC_INIT(&nsem_list);
|
|
|
|
static K_MUTEX_DEFINE(nsem_mutex);
|
|
|
|
static inline void nsem_list_lock(void)
|
|
{
|
|
__unused int ret = k_mutex_lock(&nsem_mutex, K_FOREVER);
|
|
|
|
__ASSERT(ret == 0, "nsem_list_lock() failed: %d", ret);
|
|
}
|
|
|
|
static inline void nsem_list_unlock(void)
|
|
{
|
|
k_mutex_unlock(&nsem_mutex);
|
|
}
|
|
|
|
static struct nsem_obj *nsem_find(const char *name)
|
|
{
|
|
struct nsem_obj *nsem;
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER(&nsem_list, nsem, snode) {
|
|
if ((nsem->name != NULL) && (strcmp(nsem->name, name) == 0)) {
|
|
return nsem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Clean up a named semaphore object completely (incl its `name` buffer) */
|
|
static void nsem_cleanup(struct nsem_obj *nsem)
|
|
{
|
|
if (nsem != NULL) {
|
|
if (nsem->name != NULL) {
|
|
k_free(nsem->name);
|
|
}
|
|
k_free(nsem);
|
|
}
|
|
}
|
|
|
|
/* Remove a named semaphore if it isn't unsed */
|
|
static void nsem_unref(struct nsem_obj *nsem)
|
|
{
|
|
nsem->ref_count -= 1;
|
|
__ASSERT(nsem->ref_count >= 0, "ref_count may not be negative");
|
|
|
|
if (nsem->ref_count == 0) {
|
|
__ASSERT(nsem->name == NULL, "ref_count is 0 but sem is not unlinked");
|
|
|
|
sys_slist_find_and_remove(&nsem_list, (sys_snode_t *) nsem);
|
|
|
|
/* Free nsem */
|
|
nsem_cleanup(nsem);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Destroy semaphore.
|
|
*
|
|
* see IEEE 1003.1
|
|
*/
|
|
int sem_destroy(sem_t *semaphore)
|
|
{
|
|
if (semaphore == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (k_sem_count_get(semaphore)) {
|
|
errno = EBUSY;
|
|
return -1;
|
|
}
|
|
|
|
k_sem_reset(semaphore);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Get value of semaphore.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_getvalue(sem_t *semaphore, int *value)
|
|
{
|
|
if (semaphore == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
*value = (int) k_sem_count_get(semaphore);
|
|
|
|
return 0;
|
|
}
|
|
/**
|
|
* @brief Initialize semaphore.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_init(sem_t *semaphore, int pshared, unsigned int value)
|
|
{
|
|
if (value > CONFIG_POSIX_SEM_VALUE_MAX) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Zephyr has no concept of process, so only thread shared
|
|
* semaphore makes sense in here.
|
|
*/
|
|
__ASSERT(pshared == 0, "pshared should be 0");
|
|
|
|
k_sem_init(semaphore, value, CONFIG_POSIX_SEM_VALUE_MAX);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Unlock a semaphore.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_post(sem_t *semaphore)
|
|
{
|
|
if (semaphore == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
k_sem_give(semaphore);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Try time limited locking a semaphore.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_timedwait(sem_t *semaphore, struct timespec *abstime)
|
|
{
|
|
int32_t timeout;
|
|
struct timespec current;
|
|
int64_t current_ms, abstime_ms;
|
|
|
|
__ASSERT(abstime, "abstime pointer NULL");
|
|
|
|
if ((abstime->tv_sec < 0) || (abstime->tv_nsec >= NSEC_PER_SEC)) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if (clock_gettime(CLOCK_REALTIME, ¤t) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
abstime_ms = (int64_t)_ts_to_ms(abstime);
|
|
current_ms = (int64_t)_ts_to_ms(¤t);
|
|
|
|
if (abstime_ms <= current_ms) {
|
|
timeout = 0;
|
|
} else {
|
|
timeout = (int32_t)(abstime_ms - current_ms);
|
|
}
|
|
|
|
if (k_sem_take(semaphore, K_MSEC(timeout))) {
|
|
errno = ETIMEDOUT;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Lock a semaphore if not taken.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_trywait(sem_t *semaphore)
|
|
{
|
|
if (k_sem_take(semaphore, K_NO_WAIT) == -EBUSY) {
|
|
errno = EAGAIN;
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Lock a semaphore.
|
|
*
|
|
* See IEEE 1003.1
|
|
*/
|
|
int sem_wait(sem_t *semaphore)
|
|
{
|
|
/* With K_FOREVER, may return only success. */
|
|
(void)k_sem_take(semaphore, K_FOREVER);
|
|
return 0;
|
|
}
|
|
|
|
sem_t *sem_open(const char *name, int oflags, ...)
|
|
{
|
|
va_list va;
|
|
mode_t mode;
|
|
unsigned int value;
|
|
struct nsem_obj *nsem = NULL;
|
|
size_t namelen;
|
|
|
|
va_start(va, oflags);
|
|
BUILD_ASSERT(sizeof(mode_t) <= sizeof(int));
|
|
mode = va_arg(va, int);
|
|
value = va_arg(va, unsigned int);
|
|
va_end(va);
|
|
|
|
if (value > CONFIG_POSIX_SEM_VALUE_MAX) {
|
|
errno = EINVAL;
|
|
return (sem_t *)SEM_FAILED;
|
|
}
|
|
|
|
if (name == NULL) {
|
|
errno = EINVAL;
|
|
return (sem_t *)SEM_FAILED;
|
|
}
|
|
|
|
namelen = strlen(name);
|
|
if ((namelen + 1) > CONFIG_POSIX_SEM_NAMELEN_MAX) {
|
|
errno = ENAMETOOLONG;
|
|
return (sem_t *)SEM_FAILED;
|
|
}
|
|
|
|
/* Lock before checking to make sure that the call is atomic */
|
|
nsem_list_lock();
|
|
|
|
/* Check if the named semaphore exists */
|
|
nsem = nsem_find(name);
|
|
|
|
if (nsem != NULL) { /* Named semaphore exists */
|
|
if (((oflags & O_CREAT) != 0) && ((oflags & O_EXCL) != 0)) {
|
|
errno = EEXIST;
|
|
goto error_unlock;
|
|
}
|
|
|
|
__ASSERT_NO_MSG(nsem->ref_count != INT_MAX);
|
|
nsem->ref_count++;
|
|
goto unlock;
|
|
}
|
|
|
|
/* Named sempahore doesn't exist, try to create new one */
|
|
|
|
if ((oflags & O_CREAT) == 0) {
|
|
errno = ENOENT;
|
|
goto error_unlock;
|
|
}
|
|
|
|
nsem = k_calloc(1, sizeof(struct nsem_obj));
|
|
if (nsem == NULL) {
|
|
errno = ENOSPC;
|
|
goto error_unlock;
|
|
}
|
|
|
|
/* goto `cleanup_error_unlock` past this point to avoid memory leak */
|
|
|
|
nsem->name = k_calloc(namelen + 1, sizeof(uint8_t));
|
|
if (nsem->name == NULL) {
|
|
errno = ENOSPC;
|
|
goto cleanup_error_unlock;
|
|
}
|
|
|
|
strcpy(nsem->name, name);
|
|
|
|
/* 1 for this open instance, +1 for the linked name */
|
|
nsem->ref_count = 2;
|
|
|
|
(void)k_sem_init(&nsem->sem, value, CONFIG_POSIX_SEM_VALUE_MAX);
|
|
|
|
sys_slist_append(&nsem_list, (sys_snode_t *)&(nsem->snode));
|
|
|
|
goto unlock;
|
|
|
|
cleanup_error_unlock:
|
|
nsem_cleanup(nsem);
|
|
|
|
error_unlock:
|
|
nsem = NULL;
|
|
|
|
unlock:
|
|
nsem_list_unlock();
|
|
return nsem == NULL ? SEM_FAILED : &nsem->sem;
|
|
}
|
|
|
|
int sem_unlink(const char *name)
|
|
{
|
|
int ret = 0;
|
|
struct nsem_obj *nsem;
|
|
|
|
if (name == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if ((strlen(name) + 1) > CONFIG_POSIX_SEM_NAMELEN_MAX) {
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
|
|
nsem_list_lock();
|
|
|
|
/* Check if queue already exists */
|
|
nsem = nsem_find(name);
|
|
if (nsem == NULL) {
|
|
ret = -1;
|
|
errno = ENOENT;
|
|
goto unlock;
|
|
}
|
|
|
|
k_free(nsem->name);
|
|
nsem->name = NULL;
|
|
nsem_unref(nsem);
|
|
|
|
unlock:
|
|
nsem_list_unlock();
|
|
return ret;
|
|
}
|
|
|
|
int sem_close(sem_t *sem)
|
|
{
|
|
struct nsem_obj *nsem = CONTAINER_OF(sem, struct nsem_obj, sem);
|
|
|
|
if (sem == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
nsem_list_lock();
|
|
nsem_unref(nsem);
|
|
nsem_list_unlock();
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_ZTEST
|
|
/* Used by ztest to get the ref count of a named semaphore */
|
|
int nsem_get_ref_count(sem_t *sem)
|
|
{
|
|
struct nsem_obj *nsem = CONTAINER_OF(sem, struct nsem_obj, sem);
|
|
int ref_count;
|
|
|
|
__ASSERT_NO_MSG(sem != NULL);
|
|
__ASSERT_NO_MSG(nsem != NULL);
|
|
|
|
nsem_list_lock();
|
|
ref_count = nsem->ref_count;
|
|
nsem_list_unlock();
|
|
|
|
return ref_count;
|
|
}
|
|
|
|
/* Used by ztest to get the length of the named semaphore */
|
|
size_t nsem_get_list_len(void)
|
|
{
|
|
size_t len;
|
|
|
|
nsem_list_lock();
|
|
len = sys_slist_len(&nsem_list);
|
|
nsem_list_unlock();
|
|
|
|
return len;
|
|
}
|
|
#endif
|