From 9e9e61d27d146e2ca83d5b0f590683a3f605c3f1 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 28 Jul 2015 13:46:56 +0200 Subject: score: Add self-contained condition implementation --- cpukit/libmisc/monitor/mon-prmisc.c | 1 + cpukit/score/Makefile.am | 1 + cpukit/score/include/rtems/score/statesimpl.h | 3 + cpukit/score/src/condition.c | 309 +++++++++++++++++++++++++ testsuites/sptests/spsyslock01/init.c | 104 +++++++++ testsuites/sptests/spsyslock01/spsyslock01.doc | 9 + 6 files changed, 427 insertions(+) create mode 100644 cpukit/score/src/condition.c diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c index 14ba173256..a3d7a663be 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 }, + { "Wslcnd", STATES_WAITING_FOR_SYS_LOCK_CONDITION, 0 }, { "Wslftx", STATES_WAITING_FOR_SYS_LOCK_FUTEX, 0 }, { "Wslmtx", STATES_WAITING_FOR_SYS_LOCK_MUTEX, 0 }, { "Wslsem", STATES_WAITING_FOR_SYS_LOCK_SEMAPHORE, 0 }, diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index 7c95ef984d..03ceb7aff9 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -344,6 +344,7 @@ libscore_a_SOURCES += src/apiext.c src/chain.c src/chainappend.c \ src/debugisthreaddispatchingallowed.c \ src/interr.c src/isr.c src/wkspace.c src/wkstringduplicate.c libscore_a_SOURCES += src/isrisinprogress.c +libscore_a_SOURCES += src/condition.c libscore_a_SOURCES += src/debugisownerofallocator.c libscore_a_SOURCES += src/futex.c libscore_a_SOURCES += src/profilingisrentryexit.c diff --git a/cpukit/score/include/rtems/score/statesimpl.h b/cpukit/score/include/rtems/score/statesimpl.h index 82d222c490..97cadb2888 100644 --- a/cpukit/score/include/rtems/score/statesimpl.h +++ b/cpukit/score/include/rtems/score/statesimpl.h @@ -97,6 +97,8 @@ extern "C" { * blocking state. */ #define STATES_INTERRUPTIBLE_BY_SIGNAL 0x10000000 +/** This macro corresponds to a task waiting for a condition. */ +#define STATES_WAITING_FOR_SYS_LOCK_CONDITION 0x20000000 /** This macro corresponds to a task waiting for a local object operation. */ #define STATES_LOCALLY_BLOCKED ( STATES_WAITING_FOR_BUFFER | \ @@ -112,6 +114,7 @@ extern "C" { STATES_WAITING_FOR_SYS_LOCK_MUTEX | \ STATES_WAITING_FOR_SYS_LOCK_SEMAPHORE | \ STATES_WAITING_FOR_SYS_LOCK_FUTEX | \ + STATES_WAITING_FOR_SYS_LOCK_CONDITION | \ STATES_WAITING_FOR_RWLOCK ) /** This macro corresponds to a task waiting which is blocked. */ diff --git a/cpukit/score/src/condition.c b/cpukit/score/src/condition.c new file mode 100644 index 0000000000..22c2a9b97c --- /dev/null +++ b/cpukit/score/src/condition.c @@ -0,0 +1,309 @@ +/* + * 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 +#include +#include + +#define CONDITION_TQ_OPERATIONS &_Thread_queue_Operations_FIFO + +typedef struct { + Thread_queue_Syslock_queue Queue; +} Condition_Control; + +RTEMS_STATIC_ASSERT( + offsetof( Condition_Control, Queue ) + == offsetof( struct _Condition_Control, _Queue ), + CONDITION_CONTROL_QUEUE +); + +RTEMS_STATIC_ASSERT( + sizeof( Condition_Control ) == sizeof( struct _Condition_Control ), + CONDITION_CONTROL_SIZE +); + +static Condition_Control *_Condition_Get( + struct _Condition_Control *_condition +) +{ + return (Condition_Control *) _condition; +} + +static Thread_Control *_Condition_Queue_acquire_critical( + Condition_Control *condition, + ISR_lock_Context *lock_context +) +{ + Thread_Control *executing; + + executing = _Thread_Executing; + _Thread_queue_Queue_acquire_critical( + &condition->Queue.Queue, + &executing->Potpourri_stats, + lock_context + ); + + return executing; +} + +static void _Condition_Queue_release( + Condition_Control *condition, + ISR_lock_Context *lock_context +) +{ + _Thread_queue_Queue_release( &condition->Queue.Queue, lock_context ); +} + +static Per_CPU_Control *_Condition_Do_wait( + struct _Condition_Control *_condition, + Watchdog_Interval timeout, + ISR_lock_Context *lock_context +) +{ + Condition_Control *condition; + Thread_Control *executing; + Per_CPU_Control *cpu_self; + + condition = _Condition_Get( _condition ); + executing = _Condition_Queue_acquire_critical( condition, lock_context ); + cpu_self = _Thread_Dispatch_disable_critical( lock_context ); + + executing->Wait.return_code = 0; + _Thread_queue_Enqueue_critical( + &condition->Queue.Queue, + CONDITION_TQ_OPERATIONS, + executing, + STATES_WAITING_FOR_SYS_LOCK_CONDITION, + timeout, + ETIMEDOUT, + lock_context + ); + + return cpu_self; +} + +void _Condition_Wait( + struct _Condition_Control *_condition, + struct _Mutex_Control *_mutex +) +{ + ISR_lock_Context lock_context; + Per_CPU_Control *cpu_self; + + _ISR_lock_ISR_disable( &lock_context ); + cpu_self = _Condition_Do_wait( _condition, 0, &lock_context ); + + _Mutex_Release( _mutex ); + _Thread_Dispatch_enable( cpu_self ); + _Mutex_Acquire( _mutex ); +} + +int _Condition_Wait_timed( + struct _Condition_Control *_condition, + struct _Mutex_Control *_mutex, + const struct timespec *abstime +) +{ + ISR_lock_Context lock_context; + Per_CPU_Control *cpu_self; + Thread_Control *executing; + int eno; + Watchdog_Interval ticks; + + _ISR_lock_ISR_disable( &lock_context ); + + switch ( _TOD_Absolute_timeout_to_ticks( abstime, &ticks ) ) { + case TOD_ABSOLUTE_TIMEOUT_INVALID: + _ISR_lock_ISR_enable( &lock_context ); + return EINVAL; + case TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST: + case TOD_ABSOLUTE_TIMEOUT_IS_NOW: + _ISR_lock_ISR_enable( &lock_context ); + return ETIMEDOUT; + default: + break; + } + + cpu_self = _Condition_Do_wait( _condition, ticks, &lock_context ); + + _Mutex_Release( _mutex ); + executing = cpu_self->executing; + _Thread_Dispatch_enable( cpu_self ); + eno = (int) executing->Wait.return_code; + _Mutex_Acquire( _mutex ); + + return eno; +} + +void _Condition_Wait_recursive( + struct _Condition_Control *_condition, + struct _Mutex_recursive_Control *_mutex +) +{ + ISR_lock_Context lock_context; + Per_CPU_Control *cpu_self; + unsigned int nest_level; + + _ISR_lock_ISR_disable( &lock_context ); + cpu_self = _Condition_Do_wait( _condition, 0, &lock_context ); + + nest_level = _mutex->_nest_level; + _mutex->_nest_level = 0; + _Mutex_recursive_Release( _mutex ); + _Thread_Dispatch_enable( cpu_self ); + _Mutex_recursive_Acquire( _mutex ); + _mutex->_nest_level = nest_level; +} + +int _Condition_Wait_recursive_timed( + struct _Condition_Control *_condition, + struct _Mutex_recursive_Control *_mutex, + const struct timespec *abstime +) +{ + ISR_lock_Context lock_context; + Per_CPU_Control *cpu_self; + Thread_Control *executing; + int eno; + unsigned int nest_level; + Watchdog_Interval ticks; + + _ISR_lock_ISR_disable( &lock_context ); + + switch ( _TOD_Absolute_timeout_to_ticks( abstime, &ticks ) ) { + case TOD_ABSOLUTE_TIMEOUT_INVALID: + _ISR_lock_ISR_enable( &lock_context ); + return EINVAL; + case TOD_ABSOLUTE_TIMEOUT_IS_IN_PAST: + case TOD_ABSOLUTE_TIMEOUT_IS_NOW: + _ISR_lock_ISR_enable( &lock_context ); + return ETIMEDOUT; + default: + break; + } + + cpu_self = _Condition_Do_wait( _condition, ticks, &lock_context ); + + nest_level = _mutex->_nest_level; + _mutex->_nest_level = 0; + _Mutex_recursive_Release( _mutex ); + executing = cpu_self->executing; + _Thread_Dispatch_enable( cpu_self ); + eno = (int) executing->Wait.return_code; + _Mutex_recursive_Acquire( _mutex ); + _mutex->_nest_level = nest_level; + + return eno; +} + +static int _Condition_Wake( struct _Condition_Control *_condition, int count ) +{ + Condition_Control *condition; + ISR_lock_Context lock_context; + Thread_queue_Heads *heads; + Chain_Control unblock; + Chain_Node *node; + Chain_Node *tail; + int woken; + + condition = _Condition_Get( _condition ); + _ISR_lock_ISR_disable( &lock_context ); + _Condition_Queue_acquire_critical( condition, &lock_context ); + + /* + * In common uses cases of condition variables there are normally no threads + * on the queue, so check this condition early. + */ + heads = condition->Queue.Queue.heads; + if ( __predict_true( heads == NULL ) ) { + _Condition_Queue_release( condition, &lock_context ); + + return 0; + } + + woken = 0; + _Chain_Initialize_empty( &unblock ); + while ( count > 0 && heads != NULL ) { + const Thread_queue_Operations *operations; + Thread_Control *first; + bool do_unblock; + + operations = CONDITION_TQ_OPERATIONS; + first = ( *operations->first )( heads ); + + do_unblock = _Thread_queue_Extract_locked( + &condition->Queue.Queue, + operations, + first + ); + if (do_unblock) { + _Chain_Append_unprotected( &unblock, &first->Wait.Node.Chain ); + } + + ++woken; + --count; + heads = condition->Queue.Queue.heads; + } + + node = _Chain_First( &unblock ); + tail = _Chain_Tail( &unblock ); + if ( node != tail ) { + Per_CPU_Control *cpu_self; + + cpu_self = _Thread_Dispatch_disable_critical( &lock_context ); + _Condition_Queue_release( condition, &lock_context ); + + do { + Thread_Control *thread; + Chain_Node *next; + + next = _Chain_Next( node ); + thread = THREAD_CHAIN_NODE_TO_THREAD( node ); + _Watchdog_Remove_ticks( &thread->Timer ); + _Thread_Unblock( thread ); + + node = next; + } while ( node != tail ); + + _Thread_Dispatch_enable( cpu_self ); + } else { + _Condition_Queue_release( condition, &lock_context ); + } + + return woken; +} + +void _Condition_Signal( struct _Condition_Control *_condition ) +{ + _Condition_Wake( _condition, 1 ); +} + +void _Condition_Broadcast( struct _Condition_Control *_condition ) +{ + _Condition_Wake( _condition, INT_MAX ); +} + +#endif /* HAVE_STRUCT__THREAD_QUEUE_QUEUE */ diff --git a/testsuites/sptests/spsyslock01/init.c b/testsuites/sptests/spsyslock01/init.c index ec78430eea..c73fca7ede 100644 --- a/testsuites/sptests/spsyslock01/init.c +++ b/testsuites/sptests/spsyslock01/init.c @@ -46,12 +46,17 @@ const char rtems_test_name[] = "SPSYSLOCK 1"; #define EVENT_FUTEX_WAIT RTEMS_EVENT_8 +#define EVENT_CONDITION_WAIT RTEMS_EVENT_9 + +#define EVENT_CONDITION_WAIT_REC RTEMS_EVENT_10 + typedef struct { rtems_id high[2]; rtems_id mid; rtems_id low; struct _Mutex_Control mtx; struct _Mutex_recursive_Control rec_mtx; + struct _Condition_Control cond; struct _Semaphore_Control sem; struct _Futex_Control futex; int val; @@ -93,21 +98,25 @@ static void test_initialization(test_context *ctx) { struct _Mutex_Control mtx = _MUTEX_INITIALIZER; struct _Mutex_recursive_Control rec_mtx = _MUTEX_RECURSIVE_INITIALIZER; + struct _Condition_Control cond = _CONDITION_INITIALIZER; struct _Semaphore_Control sem = _SEMAPHORE_INITIALIZER(1); struct _Futex_Control futex = _FUTEX_INITIALIZER; _Mutex_Initialize(&ctx->mtx); _Mutex_recursive_Initialize(&ctx->rec_mtx); + _Condition_Initialize(&ctx->cond); _Semaphore_Initialize(&ctx->sem, 1); _Futex_Initialize(&ctx->futex); rtems_test_assert(memcmp(&mtx, &ctx->mtx, sizeof(mtx)) == 0); rtems_test_assert(memcmp(&rec_mtx, &ctx->rec_mtx, sizeof(rec_mtx)) == 0); + rtems_test_assert(memcmp(&cond, &ctx->cond, sizeof(cond)) == 0); rtems_test_assert(memcmp(&sem, &ctx->sem, sizeof(sem)) == 0); rtems_test_assert(memcmp(&futex, &ctx->futex, sizeof(futex)) == 0); _Mutex_Destroy(&mtx); _Mutex_recursive_Destroy(&rec_mtx); + _Condition_Destroy(&cond); _Semaphore_Destroy(&sem); _Futex_Destroy(&futex); } @@ -287,6 +296,80 @@ static void test_mtx_timeout_recursive(test_context *ctx) send_event(ctx, idx, EVENT_REC_MTX_RELEASE); } +static void test_condition(test_context *ctx) +{ + struct _Condition_Control *cond = &ctx->cond; + size_t a = 0; + size_t b = 1; + int gen_a; + int gen_b; + + gen_a = ctx->generation[a]; + gen_b = ctx->generation[b]; + + _Condition_Signal(cond); + _Condition_Broadcast(cond); + + send_event(ctx, a, EVENT_CONDITION_WAIT); + send_event(ctx, b, EVENT_CONDITION_WAIT_REC); + + rtems_test_assert(ctx->generation[a] == gen_a + 1); + rtems_test_assert(ctx->generation[b] == gen_b + 1); + + _Condition_Signal(cond); + + rtems_test_assert(ctx->generation[a] == gen_a + 2); + rtems_test_assert(ctx->generation[b] == gen_b + 1); + + _Condition_Signal(cond); + + rtems_test_assert(ctx->generation[a] == gen_a + 2); + rtems_test_assert(ctx->generation[b] == gen_b + 2); + + send_event(ctx, a, EVENT_CONDITION_WAIT); + send_event(ctx, b, EVENT_CONDITION_WAIT_REC); + + rtems_test_assert(ctx->generation[a] == gen_a + 3); + rtems_test_assert(ctx->generation[b] == gen_b + 3); + + _Condition_Broadcast(cond); + + rtems_test_assert(ctx->generation[a] == gen_a + 4); + rtems_test_assert(ctx->generation[b] == gen_b + 4); +} + +static void test_condition_timeout(test_context *ctx) +{ + struct timespec to; + int eno; + + _Mutex_Acquire(&ctx->mtx); + memset(&to, 0x00, sizeof(to)); + eno = _Condition_Wait_timed(&ctx->cond, &ctx->mtx, &to); + rtems_test_assert(eno == ETIMEDOUT); + memset(&to, 0xff, sizeof(to)); + eno = _Condition_Wait_timed(&ctx->cond, &ctx->mtx, &to); + rtems_test_assert(eno == EINVAL); + get_abs_timeout(&to); + eno = _Condition_Wait_timed(&ctx->cond, &ctx->mtx, &to); + rtems_test_assert(eno == ETIMEDOUT); + _Mutex_Release(&ctx->mtx); + + _Mutex_recursive_Acquire(&ctx->rec_mtx); + _Mutex_recursive_Acquire(&ctx->rec_mtx); + memset(&to, 0x00, sizeof(to)); + eno = _Condition_Wait_recursive_timed(&ctx->cond, &ctx->rec_mtx, &to); + rtems_test_assert(eno == ETIMEDOUT); + memset(&to, 0xff, sizeof(to)); + eno = _Condition_Wait_recursive_timed(&ctx->cond, &ctx->rec_mtx, &to); + rtems_test_assert(eno == EINVAL); + get_abs_timeout(&to); + eno = _Condition_Wait_recursive_timed(&ctx->cond, &ctx->rec_mtx, &to); + rtems_test_assert(eno == ETIMEDOUT); + _Mutex_recursive_Release(&ctx->rec_mtx); + _Mutex_recursive_Release(&ctx->rec_mtx); +} + static void test_sem(test_context *ctx) { struct _Semaphore_Control *sem = &ctx->sem; @@ -488,6 +571,24 @@ static void high_task(rtems_task_argument idx) if ((events & EVENT_FUTEX_WAIT) != 0) { ctx->eno[idx] = _Futex_Wait(&ctx->futex, &ctx->val, 1); } + + if ((events & EVENT_CONDITION_WAIT) != 0) { + _Mutex_Acquire(&ctx->mtx); + ctx->generation[idx] = generation(ctx, idx); + _Condition_Wait(&ctx->cond, &ctx->mtx); + ctx->generation[idx] = generation(ctx, idx); + _Mutex_Release(&ctx->mtx); + } + + if ((events & EVENT_CONDITION_WAIT_REC) != 0) { + _Mutex_recursive_Acquire(&ctx->rec_mtx); + _Mutex_recursive_Acquire(&ctx->rec_mtx); + ctx->generation[idx] = generation(ctx, idx); + _Condition_Wait_recursive(&ctx->cond, &ctx->rec_mtx); + ctx->generation[idx] = generation(ctx, idx); + _Mutex_recursive_Release(&ctx->rec_mtx); + _Mutex_recursive_Release(&ctx->rec_mtx); + } } } @@ -543,6 +644,8 @@ static void test(void) test_prio_inv_recursive(ctx); test_mtx_timeout_normal(ctx); test_mtx_timeout_recursive(ctx); + test_condition(ctx); + test_condition_timeout(ctx); test_sem(ctx); test_sem_prio_wait_order(ctx); test_futex(ctx); @@ -552,6 +655,7 @@ static void test(void) _Mutex_Destroy(&ctx->mtx); _Mutex_recursive_Destroy(&ctx->rec_mtx); + _Condition_Destroy(&ctx->cond); _Semaphore_Destroy(&ctx->sem); _Futex_Destroy(&ctx->futex); } diff --git a/testsuites/sptests/spsyslock01/spsyslock01.doc b/testsuites/sptests/spsyslock01/spsyslock01.doc index c8ab182810..38b4d9e11b 100644 --- a/testsuites/sptests/spsyslock01/spsyslock01.doc +++ b/testsuites/sptests/spsyslock01/spsyslock01.doc @@ -14,6 +14,14 @@ directives: - _Mutex_recursive_Try_acquire() - _Mutex_recursive_Release() - _Mutex_recursive_Destroy() + - _Condition_Initialize() + - _Condition_Wait() + - _Condition_Wait_timed() + - _Condition_Wait_recursive() + - _Condition_Wait_recursive_timed() + - _Condition_Signal() + - _Condition_Broadcast() + - _Condition_Destroy() - _Semaphore_Initialize() - _Semaphore_Wait() - _Semaphore_Post() @@ -30,6 +38,7 @@ directives: concepts: - Ensure that self-contained mutexes and recursive mutexes work. + - Ensure that self-contained conditions work. - Ensure that self-contained semaphores work. - Ensure that self-contained futexes work. - Ensure that scheduler support works. -- cgit v1.2.3