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:
parent
8eeb5c992e
commit
f35fb33b94
5 changed files with 180 additions and 47 deletions
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in a new issue