From 214d8edd1817adc96d529c9f9fe5646f9506d682 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Sun, 28 Jun 2015 22:06:36 +0200 Subject: 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(+) create mode 100644 cpukit/score/src/mutex.c create mode 100644 testsuites/sptests/spsyslock01/Makefile.am create mode 100644 testsuites/sptests/spsyslock01/init.c create mode 100644 testsuites/sptests/spsyslock01/spsyslock01.doc create mode 100644 testsuites/sptests/spsyslock01/spsyslock01.scn diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c index 738014f8f0..ae7f75d350 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 950004d66c..c107805a3d 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 d1c402e643..4ad8786387 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 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 0000000000..f7cc12107f --- /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 + * + * + * 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 +#include + +#include +#include +#include +#include + +#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 8811623f45..d01201974f 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 99e9b892d7..be69f092b2 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 ]) +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 0000000000..7fb3a01574 --- /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@.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 0000000000..5c060898e6 --- /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 + * + * + * 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 +#include + +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 diff --git a/testsuites/sptests/spsyslock01/spsyslock01.doc b/testsuites/sptests/spsyslock01/spsyslock01.doc new file mode 100644 index 0000000000..440759d930 --- /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 0000000000..f8c05f7052 --- /dev/null +++ b/testsuites/sptests/spsyslock01/spsyslock01.scn @@ -0,0 +1,2 @@ +*** BEGIN OF TEST SPSYSLOCK 1 *** +*** END OF TEST SPSYSLOCK 1 *** -- cgit v1.2.3