tests: lib: c_lib: tests for C11 condition variables
Add tests for C11 condition variables. Signed-off-by: Christopher Friedt <cfriedt@meta.com>
This commit is contained in:
parent
7e539e2706
commit
70b03111eb
172
tests/lib/c_lib/thrd/src/cnd.c
Normal file
172
tests/lib/c_lib/thrd/src/cnd.c
Normal file
|
@ -0,0 +1,172 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Meta
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "thrd.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <threads.h>
|
||||
|
||||
#include <zephyr/ztest.h>
|
||||
|
||||
#define WAIT_TIME_MS 100
|
||||
|
||||
static struct libc_cnd_fixture {
|
||||
/* shared between threads in tests */
|
||||
cnd_t cond;
|
||||
mtx_t mutex;
|
||||
|
||||
/* de-duplicate local variables in test cases */
|
||||
int res1;
|
||||
int res2;
|
||||
thrd_t thrd1;
|
||||
thrd_t thrd2;
|
||||
bool do_timedwait;
|
||||
bool is_broadcast;
|
||||
struct timespec time_point;
|
||||
} _libc_cnd_fixture;
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_init_destroy)
|
||||
{
|
||||
/* degenerate cases */
|
||||
if (false) {
|
||||
/* pthread_cond_init() pthread_cond_destroy() are not hardened against these */
|
||||
zassert_equal(thrd_error, cnd_init(NULL));
|
||||
zassert_equal(thrd_error, cnd_init((cnd_t *)BIOS_FOOD));
|
||||
cnd_destroy(NULL);
|
||||
cnd_destroy((cnd_t *)BIOS_FOOD);
|
||||
}
|
||||
|
||||
/* happy path tested in before() / after() */
|
||||
}
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_errors)
|
||||
{
|
||||
/* degenerate test cases */
|
||||
if (false) {
|
||||
/* pthread_cond_*() are not hardened against these */
|
||||
zassert_equal(thrd_error, cnd_signal(NULL));
|
||||
zassert_equal(thrd_error, cnd_broadcast(NULL));
|
||||
zassert_equal(thrd_error, cnd_wait(NULL, NULL));
|
||||
zassert_equal(thrd_error, cnd_wait(NULL, &fixture->mutex));
|
||||
zassert_equal(thrd_error, cnd_wait(&fixture->cond, NULL));
|
||||
zassert_equal(thrd_error, cnd_timedwait(NULL, NULL, NULL));
|
||||
zassert_equal(thrd_error, cnd_timedwait(NULL, NULL, &fixture->time_point));
|
||||
zassert_equal(thrd_error, cnd_timedwait(NULL, &fixture->mutex, NULL));
|
||||
zassert_equal(thrd_error,
|
||||
cnd_timedwait(NULL, &fixture->mutex, &fixture->time_point));
|
||||
zassert_equal(thrd_error, cnd_timedwait(&fixture->cond, NULL, NULL));
|
||||
zassert_equal(thrd_error,
|
||||
cnd_timedwait(&fixture->cond, NULL, &fixture->time_point));
|
||||
zassert_equal(thrd_error, cnd_timedwait(&fixture->cond, &fixture->mutex, NULL));
|
||||
}
|
||||
}
|
||||
|
||||
static int test_cnd_thread_fn(void *arg)
|
||||
{
|
||||
int res = thrd_success;
|
||||
struct timespec time_point;
|
||||
struct libc_cnd_fixture *const fixture = arg;
|
||||
|
||||
if (fixture->do_timedwait) {
|
||||
zassume_ok(clock_gettime(CLOCK_MONOTONIC, &time_point));
|
||||
timespec_add_ms(&time_point, WAIT_TIME_MS);
|
||||
res = cnd_timedwait(&fixture->cond, &fixture->mutex, &time_point);
|
||||
} else {
|
||||
res = cnd_wait(&fixture->cond, &fixture->mutex);
|
||||
}
|
||||
|
||||
if (fixture->is_broadcast) {
|
||||
/* re-signal so that the next thread wakes up too */
|
||||
zassert_equal(thrd_success, cnd_signal(&fixture->cond));
|
||||
}
|
||||
|
||||
(void)mtx_unlock(&fixture->mutex);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static void tst_cnd_common(struct libc_cnd_fixture *fixture, size_t wait_ms, bool th2, int exp1,
|
||||
int exp2)
|
||||
{
|
||||
zassert_equal(thrd_success, mtx_lock(&fixture->mutex));
|
||||
|
||||
zassert_equal(thrd_success, thrd_create(&fixture->thrd1, test_cnd_thread_fn, fixture));
|
||||
if (th2) {
|
||||
zassert_equal(thrd_success,
|
||||
thrd_create(&fixture->thrd2, test_cnd_thread_fn, fixture));
|
||||
}
|
||||
|
||||
k_msleep(wait_ms);
|
||||
|
||||
if (fixture->is_broadcast) {
|
||||
zassert_equal(thrd_success, cnd_broadcast(&fixture->cond));
|
||||
} else {
|
||||
zassert_equal(thrd_success, cnd_signal(&fixture->cond));
|
||||
}
|
||||
|
||||
zassert_equal(thrd_success, mtx_unlock(&fixture->mutex));
|
||||
|
||||
zassert_equal(thrd_success, thrd_join(fixture->thrd1, &fixture->res1));
|
||||
if (th2) {
|
||||
zassert_equal(thrd_success, thrd_join(fixture->thrd2, &fixture->res2));
|
||||
}
|
||||
|
||||
zassert_equal(exp1, fixture->res1);
|
||||
if (th2) {
|
||||
zassert_equal(exp2, fixture->res2);
|
||||
}
|
||||
}
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_signal_wait)
|
||||
{
|
||||
tst_cnd_common(fixture, WAIT_TIME_MS / 2, false, thrd_success, DONT_CARE);
|
||||
}
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_signal_timedwait)
|
||||
{
|
||||
fixture->do_timedwait = true;
|
||||
tst_cnd_common(fixture, WAIT_TIME_MS / 2, false, thrd_success, DONT_CARE);
|
||||
}
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_timedwait_timeout)
|
||||
{
|
||||
fixture->do_timedwait = true;
|
||||
tst_cnd_common(fixture, WAIT_TIME_MS * 2, false, thrd_timedout, DONT_CARE);
|
||||
}
|
||||
|
||||
ZTEST_F(libc_cnd, test_cnd_broadcast_wait)
|
||||
{
|
||||
fixture->is_broadcast = true;
|
||||
tst_cnd_common(fixture, WAIT_TIME_MS, true, thrd_success, thrd_success);
|
||||
}
|
||||
|
||||
static void *setup(void)
|
||||
{
|
||||
return &_libc_cnd_fixture;
|
||||
}
|
||||
|
||||
static void before(void *arg)
|
||||
{
|
||||
struct libc_cnd_fixture *const fixture = arg;
|
||||
|
||||
*fixture = (struct libc_cnd_fixture){
|
||||
.res1 = FORTY_TWO,
|
||||
.res2 = SEVENTY_THREE,
|
||||
};
|
||||
|
||||
zassert_equal(thrd_success, mtx_init(&fixture->mutex, mtx_plain));
|
||||
zassert_equal(thrd_success, cnd_init(&fixture->cond));
|
||||
}
|
||||
|
||||
static void after(void *arg)
|
||||
{
|
||||
struct libc_cnd_fixture *const fixture = arg;
|
||||
|
||||
cnd_destroy(&fixture->cond);
|
||||
mtx_destroy(&fixture->mutex);
|
||||
}
|
||||
|
||||
ZTEST_SUITE(libc_cnd, NULL, setup, before, after, NULL);
|
Loading…
Reference in a new issue