forked from Imagelibrary/rtems
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).
This commit is contained in:
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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. */
|
||||
|
||||
442
cpukit/score/src/mutex.c
Normal file
442
cpukit/score/src/mutex.c
Normal file
@@ -0,0 +1,442 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#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 */
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
19
testsuites/sptests/spsyslock01/Makefile.am
Normal file
19
testsuites/sptests/spsyslock01/Makefile.am
Normal file
@@ -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
|
||||
438
testsuites/sptests/spsyslock01/init.c
Normal file
438
testsuites/sptests/spsyslock01/init.c
Normal file
@@ -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 <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>
|
||||
20
testsuites/sptests/spsyslock01/spsyslock01.doc
Normal file
20
testsuites/sptests/spsyslock01/spsyslock01.doc
Normal file
@@ -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.
|
||||
2
testsuites/sptests/spsyslock01/spsyslock01.scn
Normal file
2
testsuites/sptests/spsyslock01/spsyslock01.scn
Normal file
@@ -0,0 +1,2 @@
|
||||
*** BEGIN OF TEST SPSYSLOCK 1 ***
|
||||
*** END OF TEST SPSYSLOCK 1 ***
|
||||
Reference in New Issue
Block a user