/* SPDX-License-Identifier: BSD-2-Clause */
/*
* Copyright (c) 2015 embedded brains GmbH. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tmacros.h"
#include <threads.h>
#include <rtems/malloc.h>
const char rtems_test_name[] = "SPSTDTHREADS 1";
#define US_PER_TICK 10000
#define EVENT_MTX_LOCK RTEMS_EVENT_0
#define EVENT_MTX_UNLOCK RTEMS_EVENT_1
#define EVENT_CND_WAIT RTEMS_EVENT_2
#define EVENT_CND_TIMEDWAIT RTEMS_EVENT_3
#define EVENT_TSS RTEMS_EVENT_4
typedef struct {
rtems_id high;
rtems_id low;
once_flag once_flag;
mtx_t mtx;
cnd_t cnd;
tss_t tss;
thrd_t thrd;
int generation;
} test_context;
static test_context test_instance = {
.once_flag = ONCE_FLAG_INIT
};
static void next_generation(test_context *ctx)
{
++ctx->generation;
}
static void send_event(test_context *ctx, rtems_event_set events)
{
rtems_status_code sc;
sc = rtems_event_send(ctx->high, events);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}
static void get_abs_timeout(struct timespec *to)
{
int rv;
rv = clock_gettime(CLOCK_REALTIME, to);
rtems_test_assert(rv == 0);
to->tv_nsec += 2 * US_PER_TICK * 1000;
if (to->tv_nsec >= 1000000000) {
++to->tv_sec;
to->tv_nsec -= 1000000000;
}
}
static void test_init(test_context *ctx)
{
int status;
status = mtx_init(&ctx->mtx, mtx_plain);
rtems_test_assert(status == thrd_success);
status = cnd_init(&ctx->cnd);
rtems_test_assert(status == thrd_success);
}
static void test_destroy(test_context *ctx)
{
mtx_destroy(&ctx->mtx);
cnd_destroy(&ctx->cnd);
}
static void once_func(void)
{
test_context *ctx = &test_instance;
next_generation(ctx);
}
static void test_once(test_context *ctx)
{
int gen = ctx->generation;
call_once(&ctx->once_flag, once_func);
rtems_test_assert(ctx->generation == gen + 1);
call_once(&ctx->once_flag, once_func);
rtems_test_assert(ctx->generation == gen + 1);
}
static void test_mtx(test_context *ctx)
{
mtx_t *mtx = &ctx->mtx;
int gen = ctx->generation;
struct timespec to;
int status;
status = mtx_trylock(mtx);
rtems_test_assert(status == thrd_success);
status = mtx_lock(mtx);
rtems_test_assert(status == thrd_success);
get_abs_timeout(&to);
status = mtx_timedlock(mtx, &to);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
send_event(ctx, EVENT_MTX_LOCK);
rtems_test_assert(ctx->generation == gen + 1);
status = mtx_trylock(mtx);
rtems_test_assert(status == thrd_busy);
memset(&to, 0xff, sizeof(to));
status = mtx_timedlock(mtx, &to);
rtems_test_assert(status == thrd_error);
get_abs_timeout(&to);
status = mtx_timedlock(mtx, &to);
rtems_test_assert(status == thrd_timedout);
send_event(ctx, EVENT_MTX_UNLOCK);
rtems_test_assert(ctx->generation == gen + 2);
}
static void test_cnd(test_context *ctx)
{
cnd_t *cnd = &ctx->cnd;
mtx_t *mtx = &ctx->mtx;
int gen = ctx->generation;
struct timespec to;
int status;
send_event(ctx, EVENT_CND_WAIT);
rtems_test_assert(ctx->generation == gen + 1);
status = mtx_lock(mtx);
rtems_test_assert(status == thrd_success);
status = cnd_signal(cnd);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
rtems_test_assert(ctx->generation == gen + 2);
send_event(ctx, EVENT_CND_WAIT);
rtems_test_assert(ctx->generation == gen + 3);
status = mtx_lock(mtx);
rtems_test_assert(status == thrd_success);
status = cnd_broadcast(cnd);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
rtems_test_assert(ctx->generation == gen + 4);
status = mtx_lock(mtx);
rtems_test_assert(status == thrd_success);
memset(&to, 0xff, sizeof(to));
status = cnd_timedwait(cnd, mtx, &to);
rtems_test_assert(status == thrd_error);
get_abs_timeout(&to);
status = cnd_timedwait(cnd, mtx, &to);
rtems_test_assert(status == thrd_timedout);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
send_event(ctx, EVENT_CND_TIMEDWAIT);
rtems_test_assert(ctx->generation == gen + 5);
status = mtx_lock(mtx);
rtems_test_assert(status == thrd_success);
status = cnd_signal(cnd);
rtems_test_assert(status == thrd_success);
status = mtx_unlock(mtx);
rtems_test_assert(status == thrd_success);
rtems_test_assert(ctx->generation == gen + 6);
}
static int tss_val = TSS_DTOR_ITERATIONS;
static void tss_dtor(void *val)
{
test_context *ctx = &test_instance;
rtems_test_assert(val == &tss_val);
next_generation(ctx);
}
static void test_tss(test_context *ctx)
{
tss_dtor_t dtor = tss_dtor;
int gen = ctx->generation;
int status;
status = tss_create(&ctx->tss, dtor);
rtems_test_assert(status == thrd_success);
send_event(ctx, EVENT_TSS);
rtems_test_assert(ctx->generation == gen + 1);
tss_delete(ctx->tss);
}
static int thrd(void *arg)
{
thrd_exit(123);
}
static void test_thrd(test_context *ctx)
{
thrd_start_t thrd_start = thrd;
int status;
int exit_status;
struct timespec duration;
struct timespec remaining;
void *greedy;
rtems_test_assert(thrd_equal(rtems_task_self(), thrd_current()));
thrd_yield();
memset(&duration, 0, sizeof(duration));
duration.tv_nsec = 1;
thrd_sleep(&duration, &remaining);
rtems_test_assert(remaining.tv_sec == 0);
rtems_test_assert(remaining.tv_nsec == 0);
greedy = rtems_heap_greedy_allocate(NULL, 0);
status = thrd_create(&ctx->thrd, thrd_start, ctx);
rtems_test_assert(status == thrd_nomem);
rtems_heap_greedy_free(greedy);
status = thrd_create(&ctx->thrd, thrd_start, ctx);
rtems_test_assert(status == thrd_success);
status = thrd_create(&ctx->thrd, thrd_start, ctx);
rtems_test_assert(status == thrd_error);
exit_status = 0;
status = thrd_join(ctx->thrd, &exit_status);
rtems_test_assert(status == thrd_success);
rtems_test_assert(exit_status == 123);
status = thrd_detach(thrd_current());
rtems_test_assert(status == thrd_success);
status = thrd_detach(11235);
rtems_test_assert(status == thrd_error);
}
static void high_task(rtems_task_argument idx)
{
test_context *ctx = &test_instance;
while (true) {
rtems_event_set events;
rtems_status_code sc;
int status;
sc = rtems_event_receive(
RTEMS_ALL_EVENTS,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&events
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
if ((events & EVENT_MTX_LOCK) != 0) {
status = mtx_lock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
}
if ((events & EVENT_MTX_UNLOCK) != 0) {
status = mtx_unlock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
}
if ((events & EVENT_CND_WAIT) != 0) {
status = mtx_lock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
status = cnd_wait(&ctx->cnd, &ctx->mtx);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
status = mtx_unlock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
}
if ((events & EVENT_CND_TIMEDWAIT) != 0) {
struct timespec to;
status = mtx_lock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
get_abs_timeout(&to);
status = cnd_timedwait(&ctx->cnd, &ctx->mtx, &to);
rtems_test_assert(status == thrd_success);
next_generation(ctx);
status = mtx_unlock(&ctx->mtx);
rtems_test_assert(status == thrd_success);
}
if ((events & EVENT_TSS) != 0) {
void *val;
status = tss_set(ctx->tss, &tss_val);
rtems_test_assert(status == thrd_success);
val = tss_get(ctx->tss);
rtems_test_assert(val == &tss_val);
rtems_task_exit();
rtems_test_assert(0);
}
}
}
static void test(void)
{
test_context *ctx = &test_instance;
rtems_status_code sc;
test_init(ctx);
ctx->low = rtems_task_self();
sc = rtems_task_create(
rtems_build_name('H', 'I', 'G', 'H'),
1,
RTEMS_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&ctx->high
);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
sc = rtems_task_start(ctx->high, high_task, 0);
rtems_test_assert(sc == RTEMS_SUCCESSFUL);
test_once(ctx);
test_mtx(ctx);
test_cnd(ctx);
test_tss(ctx);
test_thrd(ctx);
test_destroy(ctx);
}
static void Init(rtems_task_argument arg)
{
TEST_BEGIN();
test();
TEST_END();
rtems_test_exit(0);
}
#define CONFIGURE_MICROSECONDS_PER_TICK US_PER_TICK
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
#define CONFIGURE_MAXIMUM_TASKS 4
#define CONFIGURE_MAXIMUM_POSIX_KEYS 1
#define CONFIGURE_MAXIMUM_POSIX_KEY_VALUE_PAIRS 1
#define CONFIGURE_MAXIMUM_POSIX_THREADS 1
#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
#define CONFIGURE_INIT_TASK_PRIORITY 4
#define CONFIGURE_INIT_TASK_INITIAL_MODES RTEMS_DEFAULT_MODES
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
#define CONFIGURE_SCHEDULER_NAME rtems_build_name('b', 'l', 'u', 'e')
#define CONFIGURE_INIT
#include <rtems/confdefs.h>