blob: aaf00f9cb7b97aab30eddceee2453159278c9ff8 [file] [log] [blame]
/*
* 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);