-
Notifications
You must be signed in to change notification settings - Fork 7.4k
posix: semaphore: implement named semaphore functions #67007
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
89b9c87
f252dda
23788b8
eb1f8c8
9396cd1
62e7378
b316cca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,78 @@ | ||
/* | ||
* 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @npitre I've modified other part that uses |
||
char *name; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We could avoid malloc if allocated like this
I think we have this pattern in many places in the code space. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm gonna gather more comments for this one, doing so will allocate a fixed sized array regardless of the actual length of the name. Alternatively we can allocate the buffer for the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say a good follow-up PR would be to add static allocation support. I'm ok with dynamic going in first and static coming in later, but I think both use cases are valuable for Zephyr. The existing pattern that doesn't use malloc should be fine for filling the static gap later. Thanks for your help ❤️ |
||
}; | ||
|
||
/* 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) | ||
{ | ||
k_mutex_lock(&nsem_mutex, K_FOREVER); | ||
} | ||
|
||
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"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @npitre This means that the following behavior is guaranteed only when
i.e.: a named semaphore can be closed without being unlinked:
|
||
|
||
sys_slist_find_and_remove(&nsem_list, (sys_snode_t *) nsem); | ||
|
||
/* Free nsem */ | ||
nsem_cleanup(nsem); | ||
} | ||
} | ||
|
||
/** | ||
* @brief Destroy semaphore. | ||
|
@@ -148,3 +215,172 @@ int sem_wait(sem_t *semaphore) | |
(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_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_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_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_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); | ||
cfriedt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🥳