posix: pthread: reimplement pthread_barrier using zephyr objects

Previously pthread_barrier_t was implemented in terms of wait
queues and internal scheduler functions.

This introduced some obstacles and inconsistency. In order
to be more consistent, rely only on Zephyr's public API and
reuse as many concepts as possible.

Deprecate `PTHREAD_BARRIER_DEFINE()` since it's non-standard.

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-06-04 09:19:46 -04:00 committed by Anas Nashif
parent 8eeb5c992e
commit f35fb33b94
5 changed files with 180 additions and 47 deletions

View file

@ -80,11 +80,7 @@ typedef struct pthread_condattr pthread_condattr_t;
BUILD_ASSERT(sizeof(pthread_condattr_t) >= sizeof(struct pthread_condattr));
/* Barrier */
typedef struct pthread_barrier {
_wait_q_t wait_q;
int max;
int count;
} pthread_barrier_t;
typedef uint32_t pthread_barrier_t;
typedef struct pthread_barrierattr {
} pthread_barrierattr_t;

View file

@ -8,7 +8,6 @@
#define ZEPHYR_INCLUDE_POSIX_PTHREAD_H_
#include <zephyr/kernel.h>
#include <zephyr/wait_q.h>
#include <zephyr/posix/time.h>
#include <zephyr/posix/unistd.h>
#include "posix_types.h"
@ -287,12 +286,9 @@ static inline int pthread_mutexattr_destroy(pthread_mutexattr_t *m)
* @param name Symbol name of the barrier
* @param count Thread count, same as the "count" argument to
* pthread_barrier_init()
* @deprecated Use @ref pthread_barrier_init instead.
*/
#define PTHREAD_BARRIER_DEFINE(name, count) \
struct pthread_barrier name = { \
.wait_q = Z_WAIT_Q_INIT(&name.wait_q), \
.max = count, \
}
#define PTHREAD_BARRIER_DEFINE(name, count) pthread_barrier_t name = -1 __DEPRECATED_MACRO
#define PTHREAD_BARRIER_SERIAL_THREAD 1
@ -308,30 +304,15 @@ int pthread_barrier_wait(pthread_barrier_t *b);
*
* See IEEE 1003.1
*/
static inline int pthread_barrier_init(pthread_barrier_t *b,
const pthread_barrierattr_t *attr,
unsigned int count)
{
ARG_UNUSED(attr);
b->max = count;
b->count = 0;
z_waitq_init(&b->wait_q);
return 0;
}
int pthread_barrier_init(pthread_barrier_t *b, const pthread_barrierattr_t *attr,
unsigned int count);
/**
* @brief POSIX threading compatibility API
*
* See IEEE 1003.1
*/
static inline int pthread_barrier_destroy(pthread_barrier_t *b)
{
ARG_UNUSED(b);
return 0;
}
int pthread_barrier_destroy(pthread_barrier_t *b);
/**
* @brief POSIX threading compatibility API

View file

@ -56,6 +56,13 @@ config MAX_PTHREAD_KEY_COUNT
help
Maximum number of simultaneously active keys in a POSIX application.
config MAX_PTHREAD_BARRIER_COUNT
int "Maximum simultaneously active barriers in a POSIX application"
default 5
range 0 255
help
Maximum number of simultaneously active keys in a POSIX application.
config SEM_VALUE_MAX
int "Maximum semaphore limit"
default 32767

View file

@ -4,31 +4,174 @@
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/init.h>
#include <zephyr/kernel.h>
#include <zephyr/posix/pthread.h>
#include <ksched.h>
#include <zephyr/wait_q.h>
#include <zephyr/sys/bitarray.h>
extern struct k_spinlock z_pthread_spinlock;
#include "posix_internal.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)
{
k_spinlock_key_t key = k_spin_lock(&z_pthread_spinlock);
int ret = 0;
int ret;
int err;
pthread_barrier_t bb = *b;
struct posix_barrier *bar;
b->count++;
if (b->count >= b->max) {
b->count = 0;
while (z_waitq_head(&b->wait_q)) {
_ready_one_thread(&b->wait_q);
bar = get_posix_barrier(bb);
if (bar == NULL) {
return EINVAL;
}
z_reschedule(&z_pthread_spinlock, key);
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;
} else {
(void) z_pend_curr(&z_pthread_spinlock, key, &b->wait_q, K_FOREVER);
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);

View file

@ -40,7 +40,7 @@ PTHREAD_COND_DEFINE(cvar0);
PTHREAD_COND_DEFINE(cvar1);
PTHREAD_BARRIER_DEFINE(barrier, N_THR_E);
static pthread_barrier_t barrier;
sem_t main_sem;
@ -244,6 +244,12 @@ ZTEST(posix_apis, test_posix_pthread_execution)
static const char thr_name[] = "thread name";
char thr_name_buf[CONFIG_THREAD_MAX_NAME_LEN];
/*
* initialize barriers the standard way after deprecating
* PTHREAD_BARRIER_DEFINE().
*/
zassert_ok(pthread_barrier_init(&barrier, NULL, N_THR_E));
sem_init(&main_sem, 0, 1);
schedparam.sched_priority = CONFIG_NUM_COOP_PRIORITIES - 1;
min_prio = sched_get_priority_min(schedpolicy);