[rtems commit] score: Add self-contained mutex implementation

Sebastian Huber sebh at rtems.org
Fri Jul 31 05:27:28 UTC 2015


Module:    rtems
Branch:    master
Commit:    214d8edd1817adc96d529c9f9fe5646f9506d682
Changeset: http://git.rtems.org/rtems/commit/?id=214d8edd1817adc96d529c9f9fe5646f9506d682

Author:    Sebastian Huber <sebastian.huber at embedded-brains.de>
Date:      Sun Jun 28 22:06:36 2015 +0200

score: Add self-contained mutex implementation

This mutex implementation uses a thread priority queue with a simple
priority inheritance mechanism (similar to the object based mutexes).
The storage space must be supplied by the user (16 bytes on 32-bit
targets).

---

 cpukit/libmisc/monitor/mon-prmisc.c            |   1 +
 cpukit/score/Makefile.am                       |   1 +
 cpukit/score/include/rtems/score/statesimpl.h  |   3 +
 cpukit/score/src/mutex.c                       | 442 +++++++++++++++++++++++++
 testsuites/sptests/Makefile.am                 |   3 +
 testsuites/sptests/configure.ac                |   4 +
 testsuites/sptests/spsyslock01/Makefile.am     |  19 ++
 testsuites/sptests/spsyslock01/init.c          | 438 ++++++++++++++++++++++++
 testsuites/sptests/spsyslock01/spsyslock01.doc |  20 ++
 testsuites/sptests/spsyslock01/spsyslock01.scn |   2 +
 10 files changed, 933 insertions(+)

diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c
index 738014f..ae7f75d 100644
--- a/cpukit/libmisc/monitor/mon-prmisc.c
+++ b/cpukit/libmisc/monitor/mon-prmisc.c
@@ -134,6 +134,7 @@ static const rtems_assoc_t rtems_monitor_state_assoc[] = {
     { "Wseg",   STATES_WAITING_FOR_SEGMENT, 0 },
     { "Wsem",   STATES_WAITING_FOR_SEMAPHORE, 0 },
     { "Wsig",   STATES_WAITING_FOR_SIGNAL, 0 },
+    { "Wslmtx", STATES_WAITING_FOR_SYS_LOCK_MUTEX, 0 },
     { "Wsysev", STATES_WAITING_FOR_SYSTEM_EVENT, 0 },
     { "Wterm",  STATES_WAITING_FOR_TERMINATION, 0 },
     { "Wtime",  STATES_WAITING_FOR_TIME, 0 },
diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am
index 950004d..c107805 100644
--- a/cpukit/score/Makefile.am
+++ b/cpukit/score/Makefile.am
@@ -346,6 +346,7 @@ libscore_a_SOURCES += src/apiext.c src/chain.c src/chainappend.c \
 libscore_a_SOURCES += src/isrisinprogress.c
 libscore_a_SOURCES += src/debugisownerofallocator.c
 libscore_a_SOURCES += src/profilingisrentryexit.c
+libscore_a_SOURCES += src/mutex.c
 libscore_a_SOURCES += src/once.c
 libscore_a_SOURCES += src/resourceiterate.c
 libscore_a_SOURCES += src/smpbarrierwait.c
diff --git a/cpukit/score/include/rtems/score/statesimpl.h b/cpukit/score/include/rtems/score/statesimpl.h
index d1c402e..4ad8786 100644
--- a/cpukit/score/include/rtems/score/statesimpl.h
+++ b/cpukit/score/include/rtems/score/statesimpl.h
@@ -86,6 +86,8 @@ extern "C" {
 #define STATES_RESTARTING                      0x800000
 /** This macro corresponds to a task waiting for a join. */
 #define STATES_WAITING_FOR_JOIN                0x1000000
+/** This macro corresponds to a task waiting for a <sys/lock.h> mutex. */
+#define STATES_WAITING_FOR_SYS_LOCK_MUTEX      0x2000000
 
 /** This macro corresponds to a task which is in an interruptible
  *  blocking state.
@@ -103,6 +105,7 @@ extern "C" {
                                  STATES_WAITING_FOR_SIGNAL             | \
                                  STATES_WAITING_FOR_BARRIER            | \
                                  STATES_WAITING_FOR_BSD_WAKEUP         | \
+                                 STATES_WAITING_FOR_SYS_LOCK_MUTEX     | \
                                  STATES_WAITING_FOR_RWLOCK             )
 
 /** This macro corresponds to a task waiting which is blocked. */
diff --git a/cpukit/score/src/mutex.c b/cpukit/score/src/mutex.c
new file mode 100644
index 0000000..f7cc121
--- /dev/null
+++ b/cpukit/score/src/mutex.c
@@ -0,0 +1,442 @@
+/*
+ * Copyright (c) 2015 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at 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.
+ */
+
+#if HAVE_CONFIG_H
+  #include "config.h"
+#endif
+
+#if HAVE_STRUCT__THREAD_QUEUE_QUEUE
+
+#include <sys/lock.h>
+#include <errno.h>
+
+#include <rtems/score/assert.h>
+#include <rtems/score/threadimpl.h>
+#include <rtems/score/threadqimpl.h>
+#include <rtems/score/todimpl.h>
+
+#define MUTEX_TQ_OPERATIONS &_Thread_queue_Operations_priority
+
+typedef struct {
+  Thread_queue_Syslock_queue Queue;
+  Thread_Control *owner;
+} Mutex_Control;
+
+RTEMS_STATIC_ASSERT(
+  offsetof( Mutex_Control, Queue )
+    == offsetof( struct _Mutex_Control, _Queue ),
+  MUTEX_CONTROL_QUEUE
+);
+
+RTEMS_STATIC_ASSERT(
+  offsetof( Mutex_Control, owner )
+    == offsetof( struct _Mutex_Control, _owner ),
+  MUTEX_CONTROL_OWNER
+);
+
+RTEMS_STATIC_ASSERT(
+  sizeof( Mutex_Control ) == sizeof( struct _Mutex_Control ),
+  MUTEX_CONTROL_SIZE
+);
+
+typedef struct {
+  Mutex_Control Mutex;
+  unsigned int nest_level;
+} Mutex_recursive_Control;
+
+RTEMS_STATIC_ASSERT(
+  offsetof( Mutex_recursive_Control, Mutex )
+    == offsetof( struct _Mutex_recursive_Control, _Mutex ),
+  MUTEX_RECURSIVE_CONTROL_MUTEX
+);
+
+RTEMS_STATIC_ASSERT(
+  offsetof( Mutex_recursive_Control, nest_level )
+    == offsetof( struct _Mutex_recursive_Control, _nest_level ),
+  MUTEX_RECURSIVE_CONTROL_NEST_LEVEL
+);
+
+RTEMS_STATIC_ASSERT(
+  sizeof( Mutex_recursive_Control )
+    == sizeof( struct _Mutex_recursive_Control ),
+  MUTEX_RECURSIVE_CONTROL_SIZE
+);
+
+static Mutex_Control *_Mutex_Get( struct _Mutex_Control *_mutex )
+{
+  return (Mutex_Control *) _mutex;
+}
+
+static Thread_Control *_Mutex_Queue_acquire(
+  Mutex_Control    *mutex,
+  ISR_lock_Context *lock_context
+)
+{
+  Thread_Control *executing;
+
+  _ISR_lock_ISR_disable( lock_context );
+  executing = _Thread_Executing;
+  _Thread_queue_Queue_acquire_critical(
+    &mutex->Queue.Queue,
+    &executing->Potpourri_stats,
+    lock_context
+  );
+
+  return executing;
+}
+
+static void _Mutex_Queue_release(
+  Mutex_Control    *mutex,
+  ISR_lock_Context *lock_context
+)
+{
+  _Thread_queue_Queue_release( &mutex->Queue.Queue, lock_context );
+}
+
+static void _Mutex_Acquire_slow(
+  Mutex_Control     *mutex,
+  Thread_Control    *owner,
+  Thread_Control    *executing,
+  Watchdog_Interval  timeout,
+  ISR_lock_Context  *lock_context
+)
+{
+  /* Priority inheritance */
+  _Thread_Raise_priority( owner, executing->current_priority );
+
+  _Thread_queue_Enqueue_critical(
+    &mutex->Queue.Queue,
+    MUTEX_TQ_OPERATIONS,
+    executing,
+    STATES_WAITING_FOR_SYS_LOCK_MUTEX,
+    timeout,
+    ETIMEDOUT,
+    lock_context
+  );
+}
+
+static void _Mutex_Release_slow(
+  Mutex_Control      *mutex,
+  Thread_Control     *executing,
+  Thread_queue_Heads *heads,
+  bool                keep_priority,
+  ISR_lock_Context   *lock_context
+)
+{
+  if (heads != NULL) {
+    const Thread_queue_Operations *operations;
+    Thread_Control *first;
+
+    operations = MUTEX_TQ_OPERATIONS;
+    first = ( *operations->first )( heads );
+
+    mutex->owner = first;
+    _Thread_queue_Extract_critical(
+      &mutex->Queue.Queue,
+      operations,
+      first,
+      lock_context
+    );
+  } else {
+    _Mutex_Queue_release( mutex, lock_context);
+  }
+
+  if ( !keep_priority ) {
+    Per_CPU_Control *cpu_self;
+
+    cpu_self = _Thread_Dispatch_disable();
+    _Thread_Restore_priority( executing );
+    _Thread_Dispatch_enable( cpu_self );
+  }
+}
+
+static void _Mutex_Release_critical(
+  Mutex_Control *mutex,
+  Thread_Control *executing,
+  ISR_lock_Context *lock_context
+)
+{
+  Thread_queue_Heads *heads;
+  bool keep_priority;
+
+  mutex->owner = NULL;
+
+  --executing->resource_count;
+
+  /*
+   * Ensure that the owner resource count is visible to all other
+   * processors and that we read the latest priority restore
+   * hint.
+   */
+  _Atomic_Fence( ATOMIC_ORDER_ACQ_REL );
+
+  heads = mutex->Queue.Queue.heads;
+  keep_priority = _Thread_Owns_resources( executing )
+    || !executing->priority_restore_hint;
+
+  if ( __predict_true( heads == NULL && keep_priority ) ) {
+    _Mutex_Queue_release( mutex, lock_context );
+  } else {
+    _Mutex_Release_slow(
+      mutex,
+      executing,
+      heads,
+      keep_priority,
+      lock_context
+    );
+  }
+}
+
+void _Mutex_Acquire( struct _Mutex_Control *_mutex )
+{
+  Mutex_Control    *mutex;
+  ISR_lock_Context  lock_context;
+  Thread_Control   *executing;
+  Thread_Control   *owner;
+
+  mutex = _Mutex_Get( _mutex );
+  executing = _Mutex_Queue_acquire( mutex, &lock_context );
+
+  owner = mutex->owner;
+  ++executing->resource_count;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->owner = executing;
+    _Mutex_Queue_release( mutex, &lock_context );
+  } else {
+    _Mutex_Acquire_slow( mutex, owner, executing, 0, &lock_context );
+  }
+}
+
+int _Mutex_Acquire_timed(
+  struct _Mutex_Control *_mutex,
+  const struct timespec *abstime
+)
+{
+  Mutex_Control    *mutex;
+  ISR_lock_Context  lock_context;
+  Thread_Control   *executing;
+  Thread_Control   *owner;
+
+  mutex = _Mutex_Get( _mutex );
+  executing = _Mutex_Queue_acquire( mutex, &lock_context );
+
+  owner = mutex->owner;
+  ++executing->resource_count;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->owner = executing;
+    _Mutex_Queue_release( mutex, &lock_context );
+
+    return 0;
+  } else {
+    Watchdog_Interval ticks;
+
+    switch ( _TOD_Absolute_timeout_to_ticks( abstime, &ticks ) ) {
+      case TOD_ABSOLUTE_TIMEOUT_INVALID:
+        _Mutex_Queue_release( mutex, &lock_context );
+        return EINVAL;
+      case TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST:
+      case TOD_ABSOLUTE_TIMEOUT_IS_NOW:
+        _Mutex_Queue_release( mutex, &lock_context );
+        return ETIMEDOUT;
+      default:
+        break;
+    }
+
+    executing->Wait.return_code = 0;
+    _Mutex_Acquire_slow( mutex, owner, executing, ticks, &lock_context );
+
+    return (int) executing->Wait.return_code;
+  }
+}
+
+int _Mutex_Try_acquire( struct _Mutex_Control *_mutex )
+{
+  Mutex_Control    *mutex;
+  ISR_lock_Context  lock_context;
+  Thread_Control   *executing;
+  Thread_Control   *owner;
+  int               success;
+
+  mutex = _Mutex_Get( _mutex );
+  executing = _Mutex_Queue_acquire( mutex, &lock_context );
+
+  owner = mutex->owner;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->owner = executing;
+    ++executing->resource_count;
+    success = 1;
+  } else {
+    success = 0;
+  }
+
+  _Mutex_Queue_release( mutex, &lock_context );
+
+  return success;
+}
+
+void _Mutex_Release( struct _Mutex_Control *_mutex )
+{
+  Mutex_Control    *mutex;
+  ISR_lock_Context  lock_context;
+  Thread_Control   *executing;
+
+  mutex = _Mutex_Get( _mutex );
+  executing = _Mutex_Queue_acquire( mutex, &lock_context );
+
+  _Assert( mutex->owner == executing );
+
+  _Mutex_Release_critical( mutex, executing, &lock_context );
+}
+
+static Mutex_recursive_Control *_Mutex_recursive_Get(
+  struct _Mutex_recursive_Control *_mutex
+)
+{
+  return (Mutex_recursive_Control *) _mutex;
+}
+
+void _Mutex_recursive_Acquire( struct _Mutex_recursive_Control *_mutex )
+{
+  Mutex_recursive_Control *mutex;
+  ISR_lock_Context         lock_context;
+  Thread_Control          *executing;
+  Thread_Control          *owner;
+
+  mutex = _Mutex_recursive_Get( _mutex );
+  executing = _Mutex_Queue_acquire( &mutex->Mutex, &lock_context );
+
+  owner = mutex->Mutex.owner;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->Mutex.owner = executing;
+    ++executing->resource_count;
+    _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+  } else if ( owner == executing ) {
+    ++mutex->nest_level;
+    _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+  } else {
+    _Mutex_Acquire_slow( &mutex->Mutex, owner, executing, 0, &lock_context );
+  }
+}
+
+int _Mutex_recursive_Acquire_timed(
+  struct _Mutex_recursive_Control *_mutex,
+  const struct timespec           *abstime
+)
+{
+  Mutex_recursive_Control *mutex;
+  ISR_lock_Context         lock_context;
+  Thread_Control          *executing;
+  Thread_Control          *owner;
+
+  mutex = _Mutex_recursive_Get( _mutex );
+  executing = _Mutex_Queue_acquire( &mutex->Mutex, &lock_context );
+
+  owner = mutex->Mutex.owner;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->Mutex.owner = executing;
+    ++executing->resource_count;
+    _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+
+    return 0;
+  } else if ( owner == executing ) {
+    ++mutex->nest_level;
+    _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+
+    return 0;
+  } else {
+    Watchdog_Interval ticks;
+
+    switch ( _TOD_Absolute_timeout_to_ticks( abstime, &ticks ) ) {
+      case TOD_ABSOLUTE_TIMEOUT_INVALID:
+        _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+        return EINVAL;
+      case TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST:
+      case TOD_ABSOLUTE_TIMEOUT_IS_NOW:
+        _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+        return ETIMEDOUT;
+      default:
+        break;
+    }
+
+    executing->Wait.return_code = 0;
+    _Mutex_Acquire_slow(
+      &mutex->Mutex,
+      owner,
+      executing,
+      ticks,
+      &lock_context
+    );
+
+    return (int) executing->Wait.return_code;
+  }
+}
+
+int _Mutex_recursive_Try_acquire( struct _Mutex_recursive_Control *_mutex )
+{
+  Mutex_recursive_Control *mutex;
+  ISR_lock_Context         lock_context;
+  Thread_Control          *executing;
+  Thread_Control          *owner;
+  int success;
+
+  mutex = _Mutex_recursive_Get( _mutex );
+  executing = _Mutex_Queue_acquire( &mutex->Mutex, &lock_context );
+
+  owner = mutex->Mutex.owner;
+
+  if ( __predict_true( owner == NULL ) ) {
+    mutex->Mutex.owner = executing;
+    ++executing->resource_count;
+    success = 1;
+  } else if ( owner == executing ) {
+    ++mutex->nest_level;
+    success = 1;
+  } else {
+    success = 0;
+  }
+
+  _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+
+  return success;
+}
+
+void _Mutex_recursive_Release( struct _Mutex_recursive_Control *_mutex )
+{
+  Mutex_recursive_Control *mutex;
+  ISR_lock_Context         lock_context;
+  Thread_Control          *executing;
+  unsigned int             nest_level;
+
+  mutex = _Mutex_recursive_Get( _mutex );
+  executing = _Mutex_Queue_acquire( &mutex->Mutex, &lock_context );
+
+  _Assert( mutex->Mutex.owner == executing );
+
+  nest_level = mutex->nest_level;
+
+  if ( __predict_true( nest_level == 0 ) ) {
+    _Mutex_Release_critical( &mutex->Mutex, executing, &lock_context );
+  } else {
+    mutex->nest_level = nest_level - 1;
+
+    _Mutex_Queue_release( &mutex->Mutex, &lock_context );
+  }
+}
+
+#endif /* HAVE_STRUCT__THREAD_QUEUE_QUEUE */
diff --git a/testsuites/sptests/Makefile.am b/testsuites/sptests/Makefile.am
index 8811623..d012019 100644
--- a/testsuites/sptests/Makefile.am
+++ b/testsuites/sptests/Makefile.am
@@ -37,6 +37,9 @@ if HAS_SMP
 else
 _SUBDIRS += sp29
 endif
+if HAS__THREAD_QUEUE_QUEUE
+_SUBDIRS += spsyslock01
+endif
 _SUBDIRS += sptasknopreempt01
 _SUBDIRS += spintrcritical23
 _SUBDIRS += sptimecounter01
diff --git a/testsuites/sptests/configure.ac b/testsuites/sptests/configure.ac
index 99e9b89..be69f09 100644
--- a/testsuites/sptests/configure.ac
+++ b/testsuites/sptests/configure.ac
@@ -30,6 +30,9 @@ AM_CONDITIONAL([HAS_CPLUSPLUS],[test $HAS_CPLUSPLUS = "yes"])
 # FIXME: We should get rid of this. It's a cludge.
 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")
+
 # 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")
@@ -40,6 +43,7 @@ AM_CONDITIONAL(HAS_SMP,test "$rtems_cv_RTEMS_SMP" = "yes")
 
 # Explicitly list all Makefiles here
 AC_CONFIG_FILES([Makefile
+spsyslock01/Makefile
 sptasknopreempt01/Makefile
 spintrcritical23/Makefile
 sptimecounter01/Makefile
diff --git a/testsuites/sptests/spsyslock01/Makefile.am b/testsuites/sptests/spsyslock01/Makefile.am
new file mode 100644
index 0000000..7fb3a01
--- /dev/null
+++ b/testsuites/sptests/spsyslock01/Makefile.am
@@ -0,0 +1,19 @@
+rtems_tests_PROGRAMS = spsyslock01
+spsyslock01_SOURCES = init.c
+
+dist_rtems_tests_DATA = spsyslock01.scn spsyslock01.doc
+
+include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP at .cfg
+include $(top_srcdir)/../automake/compile.am
+include $(top_srcdir)/../automake/leaf.am
+
+AM_CPPFLAGS += -I$(top_srcdir)/../support/include
+
+LINK_OBJS = $(spsyslock01_OBJECTS)
+LINK_LIBS = $(spsyslock01_LDLIBS)
+
+spsyslock01$(EXEEXT): $(spsyslock01_OBJECTS) $(spsyslock01_DEPENDENCIES)
+	@rm -f spsyslock01$(EXEEXT)
+	$(make-exe)
+
+include $(top_srcdir)/../automake/local.am
diff --git a/testsuites/sptests/spsyslock01/init.c b/testsuites/sptests/spsyslock01/init.c
new file mode 100644
index 0000000..5c06089
--- /dev/null
+++ b/testsuites/sptests/spsyslock01/init.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 2015 embedded brains GmbH.  All rights reserved.
+ *
+ *  embedded brains GmbH
+ *  Dornierstr. 4
+ *  82178 Puchheim
+ *  Germany
+ *  <rtems at 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 <sys/lock.h>
+#include <string.h>
+#include <time.h>
+
+const char rtems_test_name[] = "SPSYSLOCK 1";
+
+#define US_PER_TICK 10000
+
+#define EVENT_MTX_ACQUIRE RTEMS_EVENT_0
+
+#define EVENT_MTX_RELEASE RTEMS_EVENT_1
+
+#define EVENT_MTX_PRIO_INV RTEMS_EVENT_2
+
+#define EVENT_MTX_DEADLOCK RTEMS_EVENT_3
+
+#define EVENT_REC_MTX_ACQUIRE RTEMS_EVENT_4
+
+#define EVENT_REC_MTX_RELEASE RTEMS_EVENT_5
+
+#define EVENT_REC_MTX_PRIO_INV RTEMS_EVENT_6
+
+typedef struct {
+  rtems_id high[2];
+  rtems_id mid;
+  rtems_id low;
+  struct _Mutex_Control mtx;
+  struct _Mutex_recursive_Control rec_mtx;
+  int generation[2];
+  int current_generation[2];
+} test_context;
+
+static test_context test_instance;
+
+static int generation(test_context *ctx, size_t idx)
+{
+  return ++ctx->current_generation[idx];
+}
+
+static void send_event(test_context *ctx, size_t idx, rtems_event_set events)
+{
+  rtems_status_code sc;
+
+  sc = rtems_event_send(ctx->high[idx], 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_initialization(test_context *ctx)
+{
+  struct _Mutex_Control mtx = _MUTEX_INITIALIZER;
+  struct _Mutex_recursive_Control rec_mtx = _MUTEX_RECURSIVE_INITIALIZER;
+
+  _Mutex_Initialize(&ctx->mtx);
+  _Mutex_recursive_Initialize(&ctx->rec_mtx);
+
+  rtems_test_assert(memcmp(&mtx, &ctx->mtx, sizeof(mtx)) == 0);
+  rtems_test_assert(memcmp(&rec_mtx, &ctx->rec_mtx, sizeof(rec_mtx)) == 0);
+
+  _Mutex_Destroy(&mtx);
+  _Mutex_recursive_Destroy(&rec_mtx);
+}
+
+static void test_recursive_acquire_normal(test_context *ctx)
+{
+  struct _Mutex_Control *mtx = &ctx->mtx;
+  size_t idx = 0;
+  int success;
+
+  success = _Mutex_Try_acquire(mtx);
+  rtems_test_assert(success == 1);
+
+  success = _Mutex_Try_acquire(mtx);
+  rtems_test_assert(success == 0);
+
+  _Mutex_Release(mtx);
+
+  success = _Mutex_Try_acquire(mtx);
+  rtems_test_assert(success == 1);
+
+  _Mutex_Release(mtx);
+
+  _Mutex_Acquire(mtx);
+
+  success = _Mutex_Try_acquire(mtx);
+  rtems_test_assert(success == 0);
+
+  _Mutex_Release(mtx);
+
+  send_event(ctx, idx, EVENT_MTX_ACQUIRE);
+
+  success = _Mutex_Try_acquire(mtx);
+  rtems_test_assert(success == 0);
+
+  send_event(ctx, idx, EVENT_MTX_RELEASE);
+}
+
+static void test_recursive_acquire_recursive(test_context *ctx)
+{
+  struct _Mutex_recursive_Control *mtx = &ctx->rec_mtx;
+  size_t idx = 0;
+  int success;
+
+  success = _Mutex_recursive_Try_acquire(mtx);
+  rtems_test_assert(success == 1);
+
+  _Mutex_recursive_Acquire(mtx);
+
+  success = _Mutex_recursive_Try_acquire(mtx);
+  rtems_test_assert(success == 1);
+
+  _Mutex_recursive_Release(mtx);
+  _Mutex_recursive_Release(mtx);
+  _Mutex_recursive_Release(mtx);
+
+  send_event(ctx, idx, EVENT_REC_MTX_ACQUIRE);
+
+  success = _Mutex_recursive_Try_acquire(mtx);
+  rtems_test_assert(success == 0);
+
+  send_event(ctx, idx, EVENT_REC_MTX_RELEASE);
+}
+
+static void test_prio_acquire_order(test_context *ctx)
+{
+  struct _Mutex_Control *mtx = &ctx->mtx;
+  size_t a = 0;
+  size_t b = 1;
+  int gen_a;
+  int gen_b;
+
+  _Mutex_Acquire(mtx);
+
+  gen_a = ctx->generation[a];
+  gen_b = ctx->generation[b];
+
+  send_event(ctx, b, EVENT_MTX_ACQUIRE);
+  send_event(ctx, a, EVENT_MTX_ACQUIRE);
+
+  rtems_test_assert(ctx->generation[a] == gen_a);
+  rtems_test_assert(ctx->generation[b] == gen_b);
+
+  _Mutex_Release(mtx);
+
+  rtems_test_assert(ctx->generation[a] == gen_a + 1);
+  rtems_test_assert(ctx->generation[b] == gen_b);
+
+  send_event(ctx, a, EVENT_MTX_RELEASE);
+
+  rtems_test_assert(ctx->generation[a] == gen_a + 1);
+  rtems_test_assert(ctx->generation[b] == gen_b + 1);
+
+  send_event(ctx, b, EVENT_MTX_RELEASE);
+}
+
+static void test_prio_inv_normal(test_context *ctx)
+{
+  struct _Mutex_Control *mtx = &ctx->mtx;
+  size_t idx = 0;
+  int gen;
+
+  _Mutex_Acquire(mtx);
+  gen = ctx->generation[idx];
+  send_event(ctx, idx, EVENT_MTX_PRIO_INV);
+  rtems_test_assert(ctx->generation[idx] == gen);
+  _Mutex_Release(mtx);
+  rtems_test_assert(ctx->generation[idx] == gen + 1);
+}
+
+static void test_prio_inv_recursive(test_context *ctx)
+{
+  struct _Mutex_recursive_Control *mtx = &ctx->rec_mtx;
+  size_t idx = 0;
+  int gen;
+
+  _Mutex_recursive_Acquire(mtx);
+  gen = ctx->generation[idx];
+  send_event(ctx, idx, EVENT_REC_MTX_PRIO_INV);
+  rtems_test_assert(ctx->generation[idx] == gen);
+  _Mutex_recursive_Release(mtx);
+  rtems_test_assert(ctx->generation[idx] == gen + 1);
+}
+
+static void test_mtx_timeout_normal(test_context *ctx)
+{
+  struct _Mutex_Control *mtx = &ctx->mtx;
+  size_t idx = 0;
+  struct timespec to;
+  int eno;
+
+  eno = _Mutex_Acquire_timed(mtx, NULL);
+  rtems_test_assert(eno == 0);
+  _Mutex_Release(mtx);
+
+  send_event(ctx, idx, EVENT_MTX_ACQUIRE);
+
+  memset(&to, 0x00, sizeof(to));
+  eno = _Mutex_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == ETIMEDOUT);
+  memset(&to, 0xff, sizeof(to));
+  eno = _Mutex_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == EINVAL);
+  get_abs_timeout(&to);
+  eno = _Mutex_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == ETIMEDOUT);
+
+  send_event(ctx, idx, EVENT_MTX_RELEASE);
+}
+
+static void test_mtx_timeout_recursive(test_context *ctx)
+{
+  struct _Mutex_recursive_Control *mtx = &ctx->rec_mtx;
+  size_t idx = 0;
+  struct timespec to;
+  int eno;
+
+  eno = _Mutex_recursive_Acquire_timed(mtx, NULL);
+  rtems_test_assert(eno == 0);
+  eno = _Mutex_recursive_Acquire_timed(mtx, NULL);
+  rtems_test_assert(eno == 0);
+  _Mutex_recursive_Release(mtx);
+  _Mutex_recursive_Release(mtx);
+
+  send_event(ctx, idx, EVENT_REC_MTX_ACQUIRE);
+
+  memset(&to, 0x00, sizeof(to));
+  eno = _Mutex_recursive_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == ETIMEDOUT);
+  memset(&to, 0xff, sizeof(to));
+  eno = _Mutex_recursive_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == EINVAL);
+  get_abs_timeout(&to);
+  eno = _Mutex_recursive_Acquire_timed(mtx, &to);
+  rtems_test_assert(eno == ETIMEDOUT);
+
+  send_event(ctx, idx, EVENT_REC_MTX_RELEASE);
+}
+
+static void mid_task(rtems_task_argument arg)
+{
+  rtems_test_assert(0);
+}
+
+static void high_task(rtems_task_argument idx)
+{
+  test_context *ctx = &test_instance;
+  rtems_status_code sc;
+
+  if (idx == 0) {
+    sc = rtems_task_start(ctx->mid, mid_task, 0);
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+    sc = rtems_task_suspend(ctx->mid);
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+  }
+
+  while (true) {
+    rtems_event_set events;
+
+    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_ACQUIRE) != 0) {
+      _Mutex_Acquire(&ctx->mtx);
+      ctx->generation[idx] = generation(ctx, idx);
+    }
+
+    if ((events & EVENT_MTX_RELEASE) != 0) {
+      _Mutex_Release(&ctx->mtx);
+    }
+
+    if ((events & EVENT_MTX_PRIO_INV) != 0) {
+      sc = rtems_task_resume(ctx->mid);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+      _Mutex_Acquire(&ctx->mtx);
+      ctx->generation[idx] = generation(ctx, idx);
+      _Mutex_Release(&ctx->mtx);
+
+      sc = rtems_task_suspend(ctx->mid);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+    }
+
+    if ((events & EVENT_MTX_DEADLOCK) != 0) {
+      struct _Mutex_Control dead = _MUTEX_INITIALIZER;
+
+      _Mutex_Acquire(&dead);
+      _Mutex_Acquire(&dead);
+    }
+
+    if ((events & EVENT_REC_MTX_ACQUIRE) != 0) {
+      _Mutex_recursive_Acquire(&ctx->rec_mtx);
+    }
+
+    if ((events & EVENT_REC_MTX_RELEASE) != 0) {
+      _Mutex_recursive_Release(&ctx->rec_mtx);
+    }
+
+    if ((events & EVENT_REC_MTX_PRIO_INV) != 0) {
+      sc = rtems_task_resume(ctx->mid);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+      _Mutex_recursive_Acquire(&ctx->rec_mtx);
+      ctx->generation[idx] = generation(ctx, idx);
+      _Mutex_recursive_Release(&ctx->rec_mtx);
+
+      sc = rtems_task_suspend(ctx->mid);
+      rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+    }
+  }
+}
+
+static void test(void)
+{
+  test_context *ctx = &test_instance;
+  rtems_status_code sc;
+
+  test_initialization(ctx);
+
+  ctx->low = rtems_task_self();
+
+  sc = rtems_task_create(
+    rtems_build_name('M', 'I', 'D', ' '),
+    3,
+    RTEMS_MINIMUM_STACK_SIZE,
+    RTEMS_DEFAULT_MODES,
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &ctx->mid
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_task_create(
+    rtems_build_name('H', 'I', 'G', '0'),
+    1,
+    RTEMS_MINIMUM_STACK_SIZE,
+    RTEMS_DEFAULT_MODES,
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &ctx->high[0]
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_task_start(ctx->high[0], high_task, 0);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_task_create(
+    rtems_build_name('H', 'I', 'G', '1'),
+    2,
+    RTEMS_MINIMUM_STACK_SIZE,
+    RTEMS_DEFAULT_MODES,
+    RTEMS_DEFAULT_ATTRIBUTES,
+    &ctx->high[1]
+  );
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  sc = rtems_task_start(ctx->high[1], high_task, 1);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  test_recursive_acquire_normal(ctx);
+  test_recursive_acquire_recursive(ctx);
+  test_prio_acquire_order(ctx);
+  test_prio_inv_normal(ctx);
+  test_prio_inv_recursive(ctx);
+  test_mtx_timeout_normal(ctx);
+  test_mtx_timeout_recursive(ctx);
+
+  send_event(ctx, 0, EVENT_MTX_DEADLOCK);
+
+  _Mutex_Destroy(&ctx->mtx);
+  _Mutex_recursive_Destroy(&ctx->rec_mtx);
+}
+
+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_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_INIT
+
+#include <rtems/confdefs.h>
diff --git a/testsuites/sptests/spsyslock01/spsyslock01.doc b/testsuites/sptests/spsyslock01/spsyslock01.doc
new file mode 100644
index 0000000..440759d
--- /dev/null
+++ b/testsuites/sptests/spsyslock01/spsyslock01.doc
@@ -0,0 +1,20 @@
+This file describes the directives and concepts tested by this test set.
+
+test set name: spsyslock01
+
+directives:
+
+  - _Mutex_Initialize()
+  - _Mutex_Acquire()
+  - _Mutex_Try_acquire()
+  - _Mutex_Release()
+  - _Mutex_Destroy()
+  - _Mutex_recursive_Initialize()
+  - _Mutex_recursive_Acquire()
+  - _Mutex_recursive_Try_acquire()
+  - _Mutex_recursive_Release()
+  - _Mutex_recursive_Destroy()
+
+concepts:
+
+  - Ensure that self-contained mutexes and recursive mutexes work.
diff --git a/testsuites/sptests/spsyslock01/spsyslock01.scn b/testsuites/sptests/spsyslock01/spsyslock01.scn
new file mode 100644
index 0000000..f8c05f7
--- /dev/null
+++ b/testsuites/sptests/spsyslock01/spsyslock01.scn
@@ -0,0 +1,2 @@
+*** BEGIN OF TEST SPSYSLOCK 1 ***
+*** END OF TEST SPSYSLOCK 1 ***




More information about the vc mailing list