zephyr/lib/posix/pthread_barrier.c
Christopher Friedt 89cf4cea56 posix: pthread: mitigate include order sensitivity
Previously, the `posix_internal.h` header needed to be exposed
to the application because we had non-trivial details for
most posix types (pthread, mutex, cond, ...). Since most of
those have been simplified to a typedef'ed integer, we
no longer need to expose that header to the applicaiton.

Additionally, it means that we can adopt normalized
header order in posix.

Additionally, keep more implementation details hidden
and prefer the static keyword on internal symbols where
possible.

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
2023-06-09 12:27:04 -04:00

178 lines
3.7 KiB
C

/*
* Copyright (c) 2017 Intel Corporation
*
* 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;
}
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);