From 0c9e0726f602f8006699410104d99e7741faf85f Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 28 Sep 2021 16:10:43 +0200 Subject: validation: Test helping with suspended --- testsuites/validation/tc-score-smp-thread.c | 266 ++++++++++++++++++++++++---- 1 file changed, 236 insertions(+), 30 deletions(-) diff --git a/testsuites/validation/tc-score-smp-thread.c b/testsuites/validation/tc-score-smp-thread.c index be7e9fd1ee..0478e42262 100644 --- a/testsuites/validation/tc-score-smp-thread.c +++ b/testsuites/validation/tc-score-smp-thread.c @@ -53,6 +53,8 @@ #endif #include +#include +#include #include #include "ts-config.h" @@ -91,6 +93,20 @@ * * - Clean up all used resources. * + * - Create three worker threads and a mutex. Use the mutex and the worker to + * check that a suspended thread does not reconsider help requests. + * + * - Let worker B help worker A through the mutex. Preempt worker A. Delay + * the thread switch to worker A. + * + * - Suspend worker A and let it wait on its thread state lock. Check that + * worker A did not reconsider help requests. + * + * - Resume worker A. Check that worker A did reconsider help requests after + * the thread dispatch. + * + * - Clean up all used resources. + * * @{ */ @@ -98,16 +114,6 @@ * @brief Test context for spec:/score/thread/val/smp test case. */ typedef struct { - /** - * @brief This member contains the scheduler A identifier. - */ - rtems_id scheduler_a_id; - - /** - * @brief This member contains the scheduler B identifier. - */ - rtems_id scheduler_b_id; - /** * @brief This member contains the worker A identifier. */ @@ -137,15 +143,52 @@ typedef struct { * @brief This member contains a counter for EVENT_COUNT. */ volatile uint32_t counter; + + /** + * @brief This member contains the barrier to synchronize the runner and the + * workers. + */ + SMP_barrier_Control barrier; + + /** + * @brief This member contains the barrier state for the runner processor. + */ + SMP_barrier_State barrier_state; } ScoreThreadValSmp_Context; static ScoreThreadValSmp_Context ScoreThreadValSmp_Instance; +static void ScoreThreadValSmp_Setup( ScoreThreadValSmp_Context *ctx ) +{ + SetSelfPriority( PRIO_NORMAL ); +} + +static void ScoreThreadValSmp_Setup_Wrap( void *arg ) +{ + ScoreThreadValSmp_Context *ctx; + + ctx = arg; + ScoreThreadValSmp_Setup( ctx ); +} + +static void ScoreThreadValSmp_Teardown( ScoreThreadValSmp_Context *ctx ) +{ + RestoreRunnerPriority(); +} + +static void ScoreThreadValSmp_Teardown_Wrap( void *arg ) +{ + ScoreThreadValSmp_Context *ctx; + + ctx = arg; + ScoreThreadValSmp_Teardown( ctx ); +} + static T_fixture ScoreThreadValSmp_Fixture = { - .setup = NULL, + .setup = ScoreThreadValSmp_Setup_Wrap, .stop = NULL, - .teardown = NULL, + .teardown = ScoreThreadValSmp_Teardown_Wrap, .scope = NULL, .initial_context = &ScoreThreadValSmp_Instance }; @@ -155,11 +198,37 @@ typedef ScoreThreadValSmp_Context Context; typedef enum { EVENT_OBTAIN = RTEMS_EVENT_0, EVENT_RELEASE = RTEMS_EVENT_1, - EVENT_BUSY = RTEMS_EVENT_2, - EVENT_COUNT = RTEMS_EVENT_3, - EVENT_LET_WORKER_C_COUNT = RTEMS_EVENT_4 + EVENT_COUNT_EARLY = RTEMS_EVENT_2, + EVENT_BUSY = RTEMS_EVENT_3, + EVENT_COUNT = RTEMS_EVENT_4, + EVENT_LET_WORKER_C_COUNT = RTEMS_EVENT_5, + EVENT_SET_TASK_SWITCH_EXTENSION = RTEMS_EVENT_6 } Event; +static void TaskSwitchExtension( rtems_tcb *executing, rtems_tcb *heir ) +{ + Context *ctx; + Thread_Control *thread; + + (void) executing; + (void) heir; + + ctx = T_fixture_context(); + thread = GetThread( ctx->worker_a_id ); + + if ( thread == heir ) { + SMP_barrier_State state; + + _SMP_barrier_State_initialize( &state ); + + /* B0 */ + _SMP_barrier_Wait( &ctx->barrier, &state, 2 ); + + /* B1 */ + _SMP_barrier_Wait( &ctx->barrier, &state, 2 ); + } +} + static void WorkerTask( rtems_task_argument arg ) { Context *ctx; @@ -179,6 +248,10 @@ static void WorkerTask( rtems_task_argument arg ) ReleaseMutex( ctx->mutex_id ); } + if ( ( events & EVENT_COUNT_EARLY ) != 0 ) { + ++ctx->counter; + } + if ( ( events & EVENT_BUSY ) != 0 ) { while ( ctx->busy ) { /* Do nothing */ @@ -199,6 +272,36 @@ static void WorkerTask( rtems_task_argument arg ) /* Wait */ } } + + if ( ( events & EVENT_SET_TASK_SWITCH_EXTENSION ) != 0 ) { + SetTaskSwitchExtension( TaskSwitchExtension ); + } + } +} + +static void SchedulerBlock( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when +) +{ + Context *ctx; + + ctx = arg; + + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { + Thread_Control *thread; + + T_scheduler_set_event_handler( NULL, NULL ); + + /* B1 */ + _SMP_barrier_Wait( &ctx->barrier, &ctx->barrier_state, 2 ); + + thread = GetThread( ctx->worker_a_id ); + TicketLockWaitForOthers( &thread->Join_queue.Queue.Lock, 1 ); } } @@ -218,30 +321,29 @@ static void Resume( void *arg ) ResumeTask( thread->Object.id ); } +static void WaitForCounter( const Context *ctx, uint32_t expected ) +{ + while ( ctx->counter != expected ) { + /* Wait */ + } +} + /** * @brief Create three worker threads and a mutex. Use the mutex and the * worker to move to a helping scheduler. */ static void ScoreThreadValSmp_Action_0( ScoreThreadValSmp_Context *ctx ) { - rtems_status_code sc; - Per_CPU_Control *cpu_self; - Thread_Control *executing; + Per_CPU_Control*cpu_self; + Thread_Control *executing; executing = _Thread_Get_executing(); - SetSelfPriority( PRIO_NORMAL ); ctx->counter = 0; - sc = rtems_scheduler_ident( TEST_SCHEDULER_A_NAME, &ctx->scheduler_a_id ); - T_rsc_success( sc ); - - sc = rtems_scheduler_ident( TEST_SCHEDULER_B_NAME, &ctx->scheduler_b_id ); - T_rsc_success( sc ); - ctx->mutex_id = CreateMutex(); ctx->worker_a_id = CreateTask( "WRKA", PRIO_NORMAL ); - SetScheduler( ctx->worker_a_id, ctx->scheduler_b_id, PRIO_NORMAL ); + SetScheduler( ctx->worker_a_id, SCHEDULER_B_ID, PRIO_NORMAL ); StartTask( ctx->worker_a_id, WorkerTask, ctx ); ctx->worker_b_id = CreateTask( "WRKB", PRIO_HIGH ); @@ -276,7 +378,7 @@ static void ScoreThreadValSmp_Action_0( ScoreThreadValSmp_Context *ctx ) * the pinning of the runner thread is maintained. */ ctx->busy = false; - SetScheduler( ctx->worker_b_id, ctx->scheduler_b_id, PRIO_HIGH ); + SetScheduler( ctx->worker_b_id, SCHEDULER_B_ID, PRIO_HIGH ); SendEvents( ctx->worker_b_id, EVENT_LET_WORKER_C_COUNT ); T_eq_u32( rtems_scheduler_get_processor(), 1 ); @@ -316,8 +418,8 @@ static void ScoreThreadValSmp_Action_0( ScoreThreadValSmp_Context *ctx ) /* * Make sure the worker released the mutex. */ - SetSelfScheduler( ctx->scheduler_b_id, PRIO_LOW ); - SetSelfScheduler( ctx->scheduler_a_id, PRIO_NORMAL ); + SetSelfScheduler( SCHEDULER_B_ID, PRIO_LOW ); + SetSelfScheduler( SCHEDULER_A_ID, PRIO_NORMAL ); /* * Clean up all used resources. @@ -326,7 +428,110 @@ static void ScoreThreadValSmp_Action_0( ScoreThreadValSmp_Context *ctx ) DeleteTask( ctx->worker_b_id ); DeleteTask( ctx->worker_c_id ); DeleteMutex( ctx->mutex_id ); - RestoreRunnerPriority(); +} + +/** + * @brief Create three worker threads and a mutex. Use the mutex and the + * worker to check that a suspended thread does not reconsider help requests. + */ +static void ScoreThreadValSmp_Action_1( ScoreThreadValSmp_Context *ctx ) +{ + T_scheduler_log_10 scheduler_log; + size_t index; + const T_scheduler_event *event; + + _SMP_barrier_Control_initialize( &ctx->barrier ); + _SMP_barrier_State_initialize( &ctx->barrier_state ); + + ctx->counter = 0; + ctx->mutex_id = CreateMutex(); + + ctx->worker_a_id = CreateTask( "WRKA", PRIO_NORMAL ); + SetScheduler( ctx->worker_a_id, SCHEDULER_B_ID, PRIO_NORMAL ); + StartTask( ctx->worker_a_id, WorkerTask, ctx ); + + ctx->worker_b_id = CreateTask( "WRKB", PRIO_HIGH ); + StartTask( ctx->worker_b_id, WorkerTask, ctx ); + + ctx->worker_c_id = CreateTask( "WRKC", PRIO_NORMAL ); + SetScheduler( ctx->worker_c_id, SCHEDULER_B_ID, PRIO_HIGH ); + StartTask( ctx->worker_c_id, WorkerTask, ctx ); + + /* + * Let worker B help worker A through the mutex. Preempt worker A. Delay + * the thread switch to worker A. + */ + ctx->busy = true; + SendEvents( + ctx->worker_a_id, + EVENT_OBTAIN | EVENT_COUNT_EARLY | EVENT_BUSY | EVENT_COUNT + ); + WaitForCounter( ctx, 1 ); + + SendEvents( ctx->worker_b_id, EVENT_OBTAIN ); + SetPriority( ctx->worker_b_id, PRIO_LOW ); + SendEvents( ctx->worker_c_id, EVENT_SET_TASK_SWITCH_EXTENSION ); + + /* B0 */ + _SMP_barrier_Wait( &ctx->barrier, &ctx->barrier_state, 2 ); + + /* + * Suspend worker A and let it wait on its thread state lock. Check that + * worker A did not reconsider help requests. + */ + T_scheduler_record_10( &scheduler_log ); + T_scheduler_set_event_handler( SchedulerBlock, ctx ); + SuspendTask( ctx->worker_a_id ); + WaitForExecutionStop( ctx->worker_a_id ); + T_scheduler_record( NULL ); + T_eq_sz( scheduler_log.header.recorded, 2 ); + index = 0; + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_BLOCK ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_WITHDRAW_NODE ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_ptr( event, &T_scheduler_event_null ); + SetTaskSwitchExtension( NULL ); + + /* + * Resume worker A. Check that worker A did reconsider help requests after + * the thread dispatch. + */ + T_scheduler_record_10( &scheduler_log ); + ResumeTask( ctx->worker_a_id ); + ctx->busy = false; + WaitForCounter( ctx, 2 ); + WaitForExecutionStop( ctx->worker_a_id ); + T_scheduler_record( NULL ); + T_eq_sz( scheduler_log.header.recorded, 5 ); + index = 0; + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_UNBLOCK ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_RECONSIDER_HELP_REQUEST ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_RECONSIDER_HELP_REQUEST ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_BLOCK ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_int( event->operation, T_SCHEDULER_WITHDRAW_NODE ); + event = T_scheduler_next_any( &scheduler_log.header, &index ); + T_eq_ptr( event, &T_scheduler_event_null ); + + /* + * Clean up all used resources. + */ + SendEvents( ctx->worker_a_id, EVENT_RELEASE | EVENT_COUNT ); + WaitForCounter( ctx, 3 ); + + SetPriority( ctx->worker_b_id, PRIO_HIGH ); + SendEvents( ctx->worker_b_id, EVENT_RELEASE ); + + DeleteTask( ctx->worker_a_id ); + DeleteTask( ctx->worker_b_id ); + DeleteTask( ctx->worker_c_id ); + DeleteMutex( ctx->mutex_id ); } /** @@ -339,6 +544,7 @@ T_TEST_CASE_FIXTURE( ScoreThreadValSmp, &ScoreThreadValSmp_Fixture ) ctx = T_fixture_context(); ScoreThreadValSmp_Action_0( ctx ); + ScoreThreadValSmp_Action_1( ctx ); } /** @} */ -- cgit v1.2.3