zephyr/lib/posix/barrier.c

213 lines
4.4 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
* Copyright (c) 2023 Meta
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "posix_internal.h"
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/posix/pthread.h>
#include <zephyr/sys/bitarray.h>
struct posix_barrier {
struct k_mutex mutex;
struct k_condvar cond;
uint32_t max;
uint32_t count;
};
static struct posix_barrier posix_barrier_pool[CONFIG_MAX_PTHREAD_BARRIER_COUNT];
SYS_BITARRAY_DEFINE_STATIC(posix_barrier_bitarray, CONFIG_MAX_PTHREAD_BARRIER_COUNT);
/*
* We reserve the MSB to mark a pthread_barrier_t as initialized (from the
* perspective of the application). With a linear space, this means that
* the theoretical pthread_barrier_t range is [0,2147483647].
*/
BUILD_ASSERT(CONFIG_MAX_PTHREAD_BARRIER_COUNT < PTHREAD_OBJ_MASK_INIT,
"CONFIG_MAX_PTHREAD_BARRIER_COUNT is too high");
static inline size_t posix_barrier_to_offset(struct posix_barrier *bar)
{
return bar - posix_barrier_pool;
}
static inline size_t to_posix_barrier_idx(pthread_barrier_t b)
{
return mark_pthread_obj_uninitialized(b);
}
struct posix_barrier *get_posix_barrier(pthread_barrier_t b)
{
int actually_initialized;
size_t bit = to_posix_barrier_idx(b);
/* if the provided barrier does not claim to be initialized, its invalid */
if (!is_pthread_obj_initialized(b)) {
return NULL;
}
/* Mask off the MSB to get the actual bit index */
if (sys_bitarray_test_bit(&posix_barrier_bitarray, bit, &actually_initialized) < 0) {
return NULL;
}
if (actually_initialized == 0) {
/* The barrier claims to be initialized but is actually not */
return NULL;
}
return &posix_barrier_pool[bit];
}
int pthread_barrier_wait(pthread_barrier_t *b)
{
int ret;
int err;
pthread_barrier_t bb = *b;
struct posix_barrier *bar;
bar = get_posix_barrier(bb);
if (bar == NULL) {
return EINVAL;
}
err = k_mutex_lock(&bar->mutex, K_FOREVER);
__ASSERT_NO_MSG(err == 0);
++bar->count;
if (bar->count == bar->max) {
bar->count = 0;
ret = PTHREAD_BARRIER_SERIAL_THREAD;
goto unlock;
}
while (bar->count != 0) {
err = k_condvar_wait(&bar->cond, &bar->mutex, K_FOREVER);
__ASSERT_NO_MSG(err == 0);
/* Note: count is reset to zero by the serialized thread */
}
ret = 0;
unlock:
err = k_condvar_signal(&bar->cond);
__ASSERT_NO_MSG(err == 0);
err = k_mutex_unlock(&bar->mutex);
__ASSERT_NO_MSG(err == 0);
return ret;
}
int pthread_barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *attr,
unsigned int count)
{
size_t bit;
struct posix_barrier *bar;
if (count == 0) {
return EINVAL;
}
if (sys_bitarray_alloc(&posix_barrier_bitarray, 1, &bit) < 0) {
return ENOMEM;
}
bar = &posix_barrier_pool[bit];
bar->max = count;
bar->count = 0;
*b = mark_pthread_obj_initialized(bit);
return 0;
}
int pthread_barrier_destroy(pthread_barrier_t *b)
{
int err;
size_t bit;
struct posix_barrier *bar;
bar = get_posix_barrier(*b);
if (bar == NULL) {
return EINVAL;
}
err = k_mutex_lock(&bar->mutex, K_FOREVER);
if (err < 0) {
return -err;
}
__ASSERT_NO_MSG(err == 0);
/* An implementation may use this function to set barrier to an invalid value */
bar->max = 0;
bar->count = 0;
bit = posix_barrier_to_offset(bar);
err = sys_bitarray_free(&posix_barrier_bitarray, 1, bit);
__ASSERT_NO_MSG(err == 0);
err = k_condvar_broadcast(&bar->cond);
__ASSERT_NO_MSG(err == 0);
err = k_mutex_unlock(&bar->mutex);
__ASSERT_NO_MSG(err == 0);
return 0;
}
int pthread_barrierattr_init(pthread_barrierattr_t *attr)
{
__ASSERT_NO_MSG(attr != NULL);
attr->pshared = PTHREAD_PROCESS_PRIVATE;
return 0;
}
int pthread_barrierattr_setpshared(pthread_barrierattr_t *attr, int pshared)
{
if (pshared != PTHREAD_PROCESS_PRIVATE && pshared != PTHREAD_PROCESS_PUBLIC) {
return -EINVAL;
}
attr->pshared = pshared;
return 0;
}
int pthread_barrierattr_getpshared(const pthread_barrierattr_t *restrict attr,
int *restrict pshared)
{
*pshared = attr->pshared;
return 0;
}
int pthread_barrierattr_destroy(pthread_barrierattr_t *attr)
{
ARG_UNUSED(attr);
return 0;
}
static int pthread_barrier_pool_init(void)
{
int err;
size_t i;
for (i = 0; i < CONFIG_MAX_PTHREAD_BARRIER_COUNT; ++i) {
err = k_mutex_init(&posix_barrier_pool[i].mutex);
__ASSERT_NO_MSG(err == 0);
err = k_condvar_init(&posix_barrier_pool[i].cond);
__ASSERT_NO_MSG(err == 0);
}
return 0;
}
SYS_INIT(pthread_barrier_pool_init, PRE_KERNEL_1, 0);