From cff773f5802d0b5b4d007be3f6f4adbb04ce0d41 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Thu, 10 Sep 2015 17:12:06 +0200 Subject: libstdthreads: Add C11 threads --- testsuites/sptests/Makefile.am | 3 + testsuites/sptests/configure.ac | 4 + testsuites/sptests/spstdthreads01/Makefile.am | 19 + testsuites/sptests/spstdthreads01/init.c | 438 +++++++++++++++++++++ .../sptests/spstdthreads01/spstdthreads01.doc | 35 ++ .../sptests/spstdthreads01/spstdthreads01.scn | 2 + 6 files changed, 501 insertions(+) create mode 100644 testsuites/sptests/spstdthreads01/Makefile.am create mode 100644 testsuites/sptests/spstdthreads01/init.c create mode 100644 testsuites/sptests/spstdthreads01/spstdthreads01.doc create mode 100644 testsuites/sptests/spstdthreads01/spstdthreads01.scn (limited to 'testsuites') diff --git a/testsuites/sptests/Makefile.am b/testsuites/sptests/Makefile.am index d01201974f..688c66f97d 100644 --- a/testsuites/sptests/Makefile.am +++ b/testsuites/sptests/Makefile.am @@ -40,6 +40,9 @@ endif if HAS__THREAD_QUEUE_QUEUE _SUBDIRS += spsyslock01 endif +if HAS_THREADS_H +_SUBDIRS += spstdthreads01 +endif _SUBDIRS += sptasknopreempt01 _SUBDIRS += spintrcritical23 _SUBDIRS += sptimecounter01 diff --git a/testsuites/sptests/configure.ac b/testsuites/sptests/configure.ac index be69f092b2..ea96dbf9da 100644 --- a/testsuites/sptests/configure.ac +++ b/testsuites/sptests/configure.ac @@ -33,6 +33,9 @@ AC_CHECK_SIZEOF([time_t]) AC_CHECK_TYPES([struct _Thread_queue_Queue],[],[],[#include ]) AM_CONDITIONAL(HAS__THREAD_QUEUE_QUEUE,test x"${ac_cv_type_struct__Thread_queue_Queue}" = x"yes") +AC_CHECK_HEADERS([threads.h]) +AM_CONDITIONAL([HAS_THREADS_H],[test x"$ac_cv_header_threads_h" = x"yes"]) + # Added to newlib pthreads for RTEMS SMP (np), may not be present AC_CHECK_HEADERS([sys/cpuset.h]) AM_CONDITIONAL(HAS_CPUSET,test x"${ac_cv_header_sys_cpuset_h}" = x"yes") @@ -43,6 +46,7 @@ AM_CONDITIONAL(HAS_SMP,test "$rtems_cv_RTEMS_SMP" = "yes") # Explicitly list all Makefiles here AC_CONFIG_FILES([Makefile +spstdthreads01/Makefile spsyslock01/Makefile sptasknopreempt01/Makefile spintrcritical23/Makefile diff --git a/testsuites/sptests/spstdthreads01/Makefile.am b/testsuites/sptests/spstdthreads01/Makefile.am new file mode 100644 index 0000000000..bef6888042 --- /dev/null +++ b/testsuites/sptests/spstdthreads01/Makefile.am @@ -0,0 +1,19 @@ +rtems_tests_PROGRAMS = spstdthreads01 +spstdthreads01_SOURCES = init.c + +dist_rtems_tests_DATA = spstdthreads01.scn spstdthreads01.doc + +include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg +include $(top_srcdir)/../automake/compile.am +include $(top_srcdir)/../automake/leaf.am + +AM_CPPFLAGS += -I$(top_srcdir)/../support/include + +LINK_OBJS = $(spstdthreads01_OBJECTS) +LINK_LIBS = $(spstdthreads01_LDLIBS) + +spstdthreads01$(EXEEXT): $(spstdthreads01_OBJECTS) $(spstdthreads01_DEPENDENCIES) + @rm -f spstdthreads01$(EXEEXT) + $(make-exe) + +include $(top_srcdir)/../automake/local.am diff --git a/testsuites/sptests/spstdthreads01/init.c b/testsuites/sptests/spstdthreads01/init.c new file mode 100644 index 0000000000..40d44d6c39 --- /dev/null +++ b/testsuites/sptests/spstdthreads01/init.c @@ -0,0 +1,438 @@ +/* + * Copyright (c) 2015 embedded brains GmbH. All rights reserved. + * + * embedded brains GmbH + * Dornierstr. 4 + * 82178 Puchheim + * Germany + * + * + * The license and distribution terms for this file may be + * found in the file LICENSE in this distribution or at + * http://www.rtems.org/license/LICENSE. + */ + +#ifdef HAVE_CONFIG_H + #include "config.h" +#endif + +#include "tmacros.h" + +#include + +#include + +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); +} + +#if defined(RTEMS_POSIX_API) +static int thrd(void *arg) +{ + thrd_exit(123); +} +#endif + +static void test_thrd(test_context *ctx) +{ +#if defined(RTEMS_POSIX_API) + 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); +#endif +} + +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_delete(RTEMS_SELF); + 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_CONSOLE_DRIVER + +#define CONFIGURE_USE_IMFS_AS_BASE_FILESYSTEM + +#define CONFIGURE_MAXIMUM_TASKS 4 + +#define CONFIGURE_MAXIMUM_POSIX_KEYS 1 +#define CONFIGURE_MAXIMUM_POSIX_KEY_VALUE_PAIRS 1 + +#if defined(RTEMS_POSIX_API) +#define CONFIGURE_MAXIMUM_POSIX_THREADS 1 +#endif + +#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 diff --git a/testsuites/sptests/spstdthreads01/spstdthreads01.doc b/testsuites/sptests/spstdthreads01/spstdthreads01.doc new file mode 100644 index 0000000000..15943745fc --- /dev/null +++ b/testsuites/sptests/spstdthreads01/spstdthreads01.doc @@ -0,0 +1,35 @@ +This file describes the directives and concepts tested by this test set. + +test set name: spstdthreads01 + +directives: + + - call_once() + - cnd_broadcast() + - cnd_destroy() + - cnd_init() + - cnd_signal() + - cnd_timedwait() + - cnd_wait() + - mtx_destroy() + - mtx_init() + - mtx_lock() + - mtx_timedlock() + - mtx_trylock() + - mtx_unlock() + - thrd_create() + - thrd_current() + - thrd_detach() + - thrd_equal() + - thrd_exit() + - thrd_join() + - thrd_sleep() + - thrd_yield() + - tss_create() + - tss_delete() + - tss_get() + - tss_set() + +concepts: + + - Ensure that the C11 threads API works. diff --git a/testsuites/sptests/spstdthreads01/spstdthreads01.scn b/testsuites/sptests/spstdthreads01/spstdthreads01.scn new file mode 100644 index 0000000000..010272d70e --- /dev/null +++ b/testsuites/sptests/spstdthreads01/spstdthreads01.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SPSTDTHREADS 1 *** +*** END OF TEST SPSTDTHREADS 1 *** -- cgit v1.2.3