summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cpukit/Makefile.am1
-rw-r--r--cpukit/configure.ac4
-rw-r--r--cpukit/libstdthreads/Makefile.am21
-rw-r--r--cpukit/libstdthreads/call_once.c6
-rw-r--r--cpukit/libstdthreads/cnd.c31
-rw-r--r--cpukit/libstdthreads/mtx.c48
-rw-r--r--cpukit/libstdthreads/thrd.c9
-rw-r--r--cpukit/libstdthreads/tss.c7
-rw-r--r--cpukit/wrapup/Makefile.am1
-rw-r--r--testsuites/sptests/Makefile.am3
-rw-r--r--testsuites/sptests/configure.ac4
-rw-r--r--testsuites/sptests/spstdthreads01/Makefile.am19
-rw-r--r--testsuites/sptests/spstdthreads01/init.c438
-rw-r--r--testsuites/sptests/spstdthreads01/spstdthreads01.doc35
-rw-r--r--testsuites/sptests/spstdthreads01/spstdthreads01.scn2
15 files changed, 554 insertions, 75 deletions
diff --git a/cpukit/Makefile.am b/cpukit/Makefile.am
index c9a4e0f506..063a795b7f 100644
--- a/cpukit/Makefile.am
+++ b/cpukit/Makefile.am
@@ -16,6 +16,7 @@ SUBDIRS += libmisc
SUBDIRS += libmd
SUBDIRS += libgnat
SUBDIRS += libdl
+SUBDIRS += libstdthreads
SUBDIRS += wrapup
SUBDIRS += zlib
diff --git a/cpukit/configure.ac b/cpukit/configure.ac
index 14ce0f1a4c..01da86d3e4 100644
--- a/cpukit/configure.ac
+++ b/cpukit/configure.ac
@@ -141,6 +141,9 @@ AS_IF([test -n "$rtems_missing_header"],
AC_CHECK_HEADERS([semaphore.h])
AM_CONDITIONAL([HAVE_SEMAPHORE_H],[test x"$ac_cv_header_semaphore_h" = x"yes"])
+AC_CHECK_HEADERS([threads.h])
+AM_CONDITIONAL([HAVE_THREADS_H],[test x"$ac_cv_header_threads_h" = x"yes"])
+
## error out if libc doesn't provide stdint.h
AS_IF([test x"${ac_cv_header_stdint_h}" != xyes],
[AC_MSG_ERROR([Required header stdint.h not found])])
@@ -481,6 +484,7 @@ libmisc/Makefile
libi2c/Makefile
libmd/Makefile
libdl/Makefile
+libstdthreads/Makefile
zlib/Makefile
ftpd/Makefile
telnetd/Makefile
diff --git a/cpukit/libstdthreads/Makefile.am b/cpukit/libstdthreads/Makefile.am
new file mode 100644
index 0000000000..8b4ffaae0f
--- /dev/null
+++ b/cpukit/libstdthreads/Makefile.am
@@ -0,0 +1,21 @@
+include $(top_srcdir)/automake/compile.am
+
+include_HEADERS =
+
+noinst_LIBRARIES = libstdthreads.a
+
+libstdthreads_a_CFLAGS = -std=c11
+libstdthreads_a_CPPFLAGS = $(AM_CPPFLAGS)
+
+libstdthreads_a_SOURCES =
+if HAVE_THREADS_H
+libstdthreads_a_SOURCES += call_once.c
+libstdthreads_a_SOURCES += cnd.c
+libstdthreads_a_SOURCES += mtx.c
+if HAS_PTHREADS
+libstdthreads_a_SOURCES += thrd.c
+endif
+libstdthreads_a_SOURCES += tss.c
+endif
+
+include $(top_srcdir)/automake/local.am
diff --git a/cpukit/libstdthreads/call_once.c b/cpukit/libstdthreads/call_once.c
index 2d7d6ff89e..9a577d68db 100644
--- a/cpukit/libstdthreads/call_once.c
+++ b/cpukit/libstdthreads/call_once.c
@@ -26,13 +26,9 @@
* $FreeBSD r228904 2011-12-26T21:51:53Z$
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <threads.h>
#include <pthread.h>
-#include "threads.h"
-
void
call_once(once_flag *flag, void (*func)(void))
{
diff --git a/cpukit/libstdthreads/cnd.c b/cpukit/libstdthreads/cnd.c
index cccf728c77..7ed750aee4 100644
--- a/cpukit/libstdthreads/cnd.c
+++ b/cpukit/libstdthreads/cnd.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2011 Ed Schouten <ed@FreeBSD.org>
+ * Copyright (c) 2015 embedded brains GmbH <info@embedded-brains.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,20 +27,14 @@
* $FreeBSD r228904 2011-12-26T21:51:53Z$
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <threads.h>
#include <errno.h>
-#include <pthread.h>
-
-#include "threads.h"
int
cnd_broadcast(cnd_t *cond)
{
- if (pthread_cond_broadcast(cond) != 0)
- return (thrd_error);
+ _Condition_Broadcast(cond);
return (thrd_success);
}
@@ -47,29 +42,22 @@ void
cnd_destroy(cnd_t *cond)
{
- (void)pthread_cond_destroy(cond);
+ _Condition_Destroy(cond);
}
int
cnd_init(cnd_t *cond)
{
- switch (pthread_cond_init(cond, NULL)) {
- case 0:
- return (thrd_success);
- case ENOMEM:
- return (thrd_nomem);
- default:
- return (thrd_error);
- }
+ _Condition_Initialize(cond);
+ return (thrd_success);
}
int
cnd_signal(cnd_t *cond)
{
- if (pthread_cond_signal(cond) != 0)
- return (thrd_error);
+ _Condition_Signal(cond);
return (thrd_success);
}
@@ -78,7 +66,7 @@ cnd_timedwait(cnd_t *restrict cond, mtx_t *restrict mtx,
const struct timespec *restrict ts)
{
- switch (pthread_cond_timedwait(cond, mtx, ts)) {
+ switch (_Condition_Wait_recursive_timed(cond, mtx, ts)) {
case 0:
return (thrd_success);
case ETIMEDOUT:
@@ -92,7 +80,6 @@ int
cnd_wait(cnd_t *cond, mtx_t *mtx)
{
- if (pthread_cond_wait(cond, mtx) != 0)
- return (thrd_error);
+ _Condition_Wait_recursive(cond, mtx);
return (thrd_success);
}
diff --git a/cpukit/libstdthreads/mtx.c b/cpukit/libstdthreads/mtx.c
index 2d42f8a8db..48369c2ed0 100644
--- a/cpukit/libstdthreads/mtx.c
+++ b/cpukit/libstdthreads/mtx.c
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2011 Ed Schouten <ed@FreeBSD.org>
+ * Copyright (c) 2015 embedded brains GmbH <info@embedded-brains.de>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -26,46 +27,23 @@
* $FreeBSD r279326 2015-02-26T16:39:57Z$
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <threads.h>
+#include <sys/lock.h>
#include <errno.h>
-#include <pthread.h>
-
-#include "threads.h"
void
mtx_destroy(mtx_t *mtx)
{
- (void)pthread_mutex_destroy(mtx);
+ _Mutex_recursive_Destroy(mtx);
}
int
mtx_init(mtx_t *mtx, int type)
{
- pthread_mutexattr_t attr;
- int mt;
- switch (type) {
- case mtx_plain:
- case mtx_timed:
- mt = PTHREAD_MUTEX_NORMAL;
- break;
- case mtx_plain | mtx_recursive:
- case mtx_timed | mtx_recursive:
- mt = PTHREAD_MUTEX_RECURSIVE;
- break;
- default:
- return (thrd_error);
- }
-
- if (pthread_mutexattr_init(&attr) != 0)
- return (thrd_error);
- if (pthread_mutexattr_settype(&attr, mt) != 0)
- return (thrd_error);
- if (pthread_mutex_init(mtx, &attr) != 0)
- return (thrd_error);
+ (void)type;
+ _Mutex_recursive_Initialize(mtx);
return (thrd_success);
}
@@ -73,8 +51,7 @@ int
mtx_lock(mtx_t *mtx)
{
- if (pthread_mutex_lock(mtx) != 0)
- return (thrd_error);
+ _Mutex_recursive_Acquire(mtx);
return (thrd_success);
}
@@ -82,7 +59,7 @@ int
mtx_timedlock(mtx_t *restrict mtx, const struct timespec *restrict ts)
{
- switch (pthread_mutex_timedlock(mtx, ts)) {
+ switch (_Mutex_recursive_Acquire_timed(mtx, ts)) {
case 0:
return (thrd_success);
case ETIMEDOUT:
@@ -96,13 +73,11 @@ int
mtx_trylock(mtx_t *mtx)
{
- switch (pthread_mutex_trylock(mtx)) {
+ switch (_Mutex_recursive_Try_acquire(mtx)) {
case 0:
return (thrd_success);
- case EBUSY:
- return (thrd_busy);
default:
- return (thrd_error);
+ return (thrd_busy);
}
}
@@ -110,7 +85,6 @@ int
mtx_unlock(mtx_t *mtx)
{
- if (pthread_mutex_unlock(mtx) != 0)
- return (thrd_error);
+ _Mutex_recursive_Release(mtx);
return (thrd_success);
}
diff --git a/cpukit/libstdthreads/thrd.c b/cpukit/libstdthreads/thrd.c
index 562e806226..c2e439f45c 100644
--- a/cpukit/libstdthreads/thrd.c
+++ b/cpukit/libstdthreads/thrd.c
@@ -26,15 +26,12 @@
* $FreeBSD r279318 2015-02-26T09:42:03Z$
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <threads.h>
#include <pthread.h>
+#include <sched.h>
#include <stdint.h>
#include <stdlib.h>
-#include "threads.h"
-
struct thrd_param {
thrd_start_t func;
void *arg;
@@ -124,5 +121,5 @@ void
thrd_yield(void)
{
- pthread_yield();
+ sched_yield();
}
diff --git a/cpukit/libstdthreads/tss.c b/cpukit/libstdthreads/tss.c
index 0d4eea7a2e..9ff9d3bd3a 100644
--- a/cpukit/libstdthreads/tss.c
+++ b/cpukit/libstdthreads/tss.c
@@ -26,13 +26,10 @@
* $FreeBSD r228904 2011-12-26T21:51:53Z$
*/
-#include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
-
+#include <threads.h>
+#include <limits.h>
#include <pthread.h>
-#include "threads.h"
-
int
tss_create(tss_t *key, tss_dtor_t dtor)
{
diff --git a/cpukit/wrapup/Makefile.am b/cpukit/wrapup/Makefile.am
index 5fd6e33004..57ef83275c 100644
--- a/cpukit/wrapup/Makefile.am
+++ b/cpukit/wrapup/Makefile.am
@@ -20,6 +20,7 @@ TMP_LIBS += ../libgnat/libgnat.a
endif
TMP_LIBS += ../libcrypt/libcrypt.a
+TMP_LIBS += ../libstdthreads/libstdthreads.a
TMP_LIBS += ../libcsupport/libcsupport.a
TMP_LIBS += ../libcsupport/libcalloc.a
TMP_LIBS += ../libblock/libblock.a
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 <sys/lock.h>])
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
+ * <rtems@embedded-brains.de>
+ *
+ * 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 <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);
+}
+
+#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 <rtems/confdefs.h>
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 ***