posix: pthread: add option for pthread_create() barrier

To enable testing, introduce `CONFIG_PTHREAD_CREATE_BARRIER`.

Some observations were made that running several Qemu SMP targets
concurrently could lead to synchronization problems. On such
targets, it was found that the synchronization issues were
mitigated by introducing a `pthread_barrier_t` shared between
`pthread_create()` and the spawned thread.

It is suggested to enable the option when running many
SMP tests concurrently in several parallel Qemu processes,
e.g. with `twister`.

Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
Christopher Friedt 2023-06-08 19:15:11 -04:00 committed by Anas Nashif
parent 85e18746b8
commit e1f8ea1ad7
2 changed files with 46 additions and 1 deletions

View File

@ -35,6 +35,17 @@ config MAX_PTHREAD_COUNT
help
Maximum number of simultaneously active threads in a POSIX application.
config PTHREAD_CREATE_BARRIER
bool "Use a pthread_barrier_t to serialize pthread_create()"
help
When running several SMP applications in parallel instances of Qemu,
e.g. via twister, explicit serialization may be required between
pthread_create() and zephyr_thread_wrapper() when spawning and joining
many pthreads concurrently.
On such systems, say Y here to introduce explicit serialization
via pthread_barrier_wait().
config MAX_PTHREAD_MUTEX_COUNT
int "Maximum simultaneously active mutex count in POSIX application"
default 5

View File

@ -266,9 +266,18 @@ static void posix_thread_finalize(struct posix_thread *t, void *retval)
FUNC_NORETURN
static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3)
{
int err;
int barrier;
void *(*fun_ptr)(void *arg) = arg2;
struct posix_thread *t = CONTAINER_OF(k_current_get(), struct posix_thread, thread);
if (IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER)) {
/* cross the barrier so that pthread_create() can continue */
barrier = POINTER_TO_UINT(arg3);
err = pthread_barrier_wait(&barrier);
__ASSERT_NO_MSG(err == 0 || err == PTHREAD_BARRIER_SERIAL_THREAD);
}
posix_thread_finalize(t, fun_ptr(arg1));
CODE_UNREACHABLE;
@ -285,7 +294,9 @@ static void zephyr_thread_wrapper(void *arg1, void *arg2, void *arg3)
int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadroutine)(void *),
void *arg)
{
int err;
k_spinlock_key_t key;
pthread_barrier_t barrier;
struct posix_thread *safe_t;
struct posix_thread *t = NULL;
const struct pthread_attr *attr = (const struct pthread_attr *)_attr;
@ -324,6 +335,19 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
}
k_spin_unlock(&pthread_pool_lock, key);
if (IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER)) {
err = pthread_barrier_init(&barrier, NULL, 2);
if (err != 0) {
/* cannot allocate barrier. move thread back to ready_q */
key = k_spin_lock(&pthread_pool_lock);
sys_dlist_remove(&t->q_node);
sys_dlist_append(&ready_q, &t->q_node);
t->qid = POSIX_THREAD_READY_Q;
k_spin_unlock(&pthread_pool_lock, key);
t = NULL;
}
}
if (t == NULL) {
/* no threads are ready */
return EAGAIN;
@ -331,10 +355,20 @@ int pthread_create(pthread_t *th, const pthread_attr_t *_attr, void *(*threadrou
/* spawn the thread */
k_thread_create(&t->thread, attr->stack, attr->stacksize, zephyr_thread_wrapper,
(void *)arg, threadroutine, NULL,
(void *)arg, threadroutine,
IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER) ? UINT_TO_POINTER(barrier)
: NULL,
posix_to_zephyr_priority(attr->priority, attr->schedpolicy), attr->flags,
K_MSEC(attr->delayedstart));
if (IS_ENABLED(CONFIG_PTHREAD_CREATE_BARRIER)) {
/* wait for the spawned thread to cross our barrier */
err = pthread_barrier_wait(&barrier);
__ASSERT_NO_MSG(err == 0 || err == PTHREAD_BARRIER_SERIAL_THREAD);
err = pthread_barrier_destroy(&barrier);
__ASSERT_NO_MSG(err == 0);
}
/* finally provide the initialized thread to the caller */
*th = mark_pthread_obj_initialized(posix_thread_to_offset(t));