mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-02-04 12:41:34 +00:00
1405 lines
39 KiB
C
1405 lines
39 KiB
C
/* SPDX-License-Identifier: BSD-2-Clause */
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @ingroup ScoreSchedSmpValSmp
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 2021, 2022 embedded brains GmbH & Co. KG
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
/*
|
|
* This file is part of the RTEMS quality process and was automatically
|
|
* generated. If you find something that needs to be fixed or
|
|
* worded better please post a report or patch to an RTEMS mailing list
|
|
* or raise a bug report:
|
|
*
|
|
* https://www.rtems.org/bugs.html
|
|
*
|
|
* For information on updating and regenerating please refer to the How-To
|
|
* section in the Software Requirements Engineering chapter of the
|
|
* RTEMS Software Engineering manual. The manual is provided as a part of
|
|
* a release. For development sources please refer to the online
|
|
* documentation at:
|
|
*
|
|
* https://docs.rtems.org
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <rtems.h>
|
|
#include <rtems/test-scheduler.h>
|
|
#include <rtems/score/percpu.h>
|
|
#include <rtems/score/schedulersmp.h>
|
|
#include <rtems/score/threadimpl.h>
|
|
|
|
#include "tx-support.h"
|
|
|
|
#include <rtems/test.h>
|
|
|
|
/**
|
|
* @defgroup ScoreSchedSmpValSmp spec:/score/sched/smp/val/smp
|
|
*
|
|
* @ingroup TestsuitesValidationSmpOnly0
|
|
*
|
|
* @brief Tests SMP-specific scheduler behaviour.
|
|
*
|
|
* This test case performs the following actions:
|
|
*
|
|
* - Construct a system state in which a sticky thread is blocked while an idle
|
|
* thread executes on behalf of the thread.
|
|
*
|
|
* - Block the sticky worker A while it uses an idle thread in the home
|
|
* scheduler.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a thread is preempted while it is
|
|
* blocked.
|
|
*
|
|
* - Block worker A and preempt it before the withdraw node operations are
|
|
* performed for worker A.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a thread is rescheduled while it is not
|
|
* scheduled on another scheduler.
|
|
*
|
|
* - Reschedule worker A by the home scheduler while worker A is not
|
|
* scheduled on another scheduler.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which an ask for help request is cancelled
|
|
* while it is processed on another processor.
|
|
*
|
|
* - Unblock worker A. It cannot be scheduled on its home scheduler.
|
|
* Intercept the ask for help request. Block the worker A. This will
|
|
* cancel the ask for help request. Remove the request while the other
|
|
* processor tries to cancel the request.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a block operation.
|
|
*
|
|
* - Block the runner thread while the owner thread of the highest priority
|
|
* ready node is already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a block operation.
|
|
*
|
|
* - Block the runner thread while the owner thread of the highest priority
|
|
* ready node is blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a set affinity operation.
|
|
*
|
|
* - Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a set affinity operation
|
|
* while a sticky node is involved.
|
|
*
|
|
* - Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a set affinity operation.
|
|
*
|
|
* - Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a set affinity operation while a
|
|
* sticky node is involved.
|
|
*
|
|
* - Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a set priority operation.
|
|
*
|
|
* - Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a set priority operation
|
|
* while a sticky node is involved.
|
|
*
|
|
* - Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a set priority operation.
|
|
*
|
|
* - Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a yield operation.
|
|
*
|
|
* - Yield while the owner thread of the highest priority ready node is
|
|
* already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is already scheduled during a yield operation while a
|
|
* sticky node is involved.
|
|
*
|
|
* - Yield while the owner thread of the highest priority ready node is
|
|
* already scheduled.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a yield operation.
|
|
*
|
|
* - Yield while the owner thread of the highest priority ready node is
|
|
* blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Construct a system state in which a scheduler tries to schedule a node
|
|
* those owner thread is blocked during a yield operation while a sticky node
|
|
* is involved.
|
|
*
|
|
* - Yield while the owner thread of the highest priority ready node is
|
|
* blocked.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* - Create three worker threads and a mutex. Use the mutex and the worker to
|
|
* check that a not scheduled thread does not get removed from the set of
|
|
* ready threads of a scheduler when a help request is reconsidered.
|
|
*
|
|
* - Prevent that worker B can perform a post-switch cleanup.
|
|
*
|
|
* - Give worker C a lower priority than worker B. Worker B will try to
|
|
* finish the thread dispatch by doing a post-switch cleanup. The
|
|
* post-switch cleanup cannot progress since the runner owns the thread
|
|
* state lock. Wait until the other processor waits on the thread state
|
|
* lock of worker B.
|
|
*
|
|
* - Give worker C a higher priority than worker B. Let worker B do its
|
|
* post-switch cleanup which will carry out the reconsider help requests
|
|
* for a not scheduled thread.
|
|
*
|
|
* - Clean up all used resources.
|
|
*
|
|
* @{
|
|
*/
|
|
|
|
typedef enum {
|
|
WORKER_A,
|
|
WORKER_B,
|
|
WORKER_C,
|
|
WORKER_COUNT
|
|
} WorkerIndex;
|
|
|
|
/**
|
|
* @brief Test context for spec:/score/sched/smp/val/smp test case.
|
|
*/
|
|
typedef struct {
|
|
/**
|
|
* @brief This member contains the runner identifier.
|
|
*/
|
|
rtems_id runner_id;
|
|
|
|
/**
|
|
* @brief This member contains the worker identifiers.
|
|
*/
|
|
rtems_id worker_id[ WORKER_COUNT ];
|
|
|
|
/**
|
|
* @brief This member contains the mutex identifier.
|
|
*/
|
|
rtems_id mutex_id;
|
|
|
|
/**
|
|
* @brief This member contains the sticky mutex identifier.
|
|
*/
|
|
rtems_id sticky_id;
|
|
|
|
/**
|
|
* @brief This member contains the worker busy status.
|
|
*/
|
|
volatile bool busy[ WORKER_COUNT ];
|
|
|
|
/**
|
|
* @brief This member contains an ISR lock context.
|
|
*/
|
|
ISR_lock_Context lock_context;
|
|
|
|
/**
|
|
* @brief This member contains a counter.
|
|
*/
|
|
uint32_t counter;
|
|
|
|
/**
|
|
* @brief If this member is true, then the worker shall be in the busy loop.
|
|
*/
|
|
volatile bool is_busy[ WORKER_COUNT ];
|
|
|
|
/**
|
|
* @brief This member contains the per-CPU jobs.
|
|
*/
|
|
Per_CPU_Job job[ 2 ];
|
|
|
|
/**
|
|
* @brief This member contains the per-CPU job contexts.
|
|
*/
|
|
Per_CPU_Job_context job_context[ 2 ];
|
|
|
|
/**
|
|
* @brief This member contains the call within ISR request.
|
|
*/
|
|
CallWithinISRRequest request;
|
|
} ScoreSchedSmpValSmp_Context;
|
|
|
|
static ScoreSchedSmpValSmp_Context
|
|
ScoreSchedSmpValSmp_Instance;
|
|
|
|
#define EVENT_OBTAIN RTEMS_EVENT_0
|
|
|
|
#define EVENT_RELEASE RTEMS_EVENT_1
|
|
|
|
#define EVENT_STICKY_OBTAIN RTEMS_EVENT_2
|
|
|
|
#define EVENT_STICKY_RELEASE RTEMS_EVENT_3
|
|
|
|
#define EVENT_SYNC_RUNNER RTEMS_EVENT_4
|
|
|
|
#define EVENT_BUSY RTEMS_EVENT_5
|
|
|
|
typedef ScoreSchedSmpValSmp_Context Context;
|
|
|
|
static void SendAndSync(
|
|
Context *ctx,
|
|
WorkerIndex worker,
|
|
rtems_event_set event
|
|
)
|
|
{
|
|
SendEvents( ctx->worker_id[ worker ], EVENT_SYNC_RUNNER | event );
|
|
ReceiveAllEvents( EVENT_SYNC_RUNNER );
|
|
WaitForExecutionStop( ctx->worker_id[ worker ] );
|
|
}
|
|
|
|
static void MakeBusy( Context *ctx, WorkerIndex worker )
|
|
{
|
|
ctx->is_busy[ worker ] = false;
|
|
ctx->busy[ worker ] = true;
|
|
SendEvents( ctx->worker_id[ worker ], EVENT_BUSY );
|
|
}
|
|
|
|
static void WaitForBusy( Context *ctx, WorkerIndex worker )
|
|
{
|
|
while ( !ctx->is_busy[ worker ] ) {
|
|
/* Wait */
|
|
}
|
|
}
|
|
|
|
static void StopBusy( Context *ctx, WorkerIndex worker )
|
|
{
|
|
ctx->busy[ worker ] = false;
|
|
WaitForExecutionStop( ctx->worker_id[ worker ] );
|
|
}
|
|
|
|
static void MakeSticky( const Context *ctx )
|
|
{
|
|
ObtainMutex( ctx->sticky_id );
|
|
}
|
|
|
|
static void CleanSticky( const Context *ctx )
|
|
{
|
|
ReleaseMutex( ctx->sticky_id );
|
|
}
|
|
|
|
static void Block( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
SuspendTask( ctx->runner_id );
|
|
ResumeTask( ctx->runner_id );
|
|
}
|
|
|
|
static void OperationStopBusyC(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when,
|
|
T_scheduler_operation op
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if ( when == T_SCHEDULER_BEFORE && event->operation == op ) {
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
StopBusy( ctx, WORKER_C );
|
|
}
|
|
}
|
|
|
|
static void BlockStopBusyC(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationStopBusyC( arg, event, when, T_SCHEDULER_BLOCK );
|
|
}
|
|
|
|
static void SetAffinityStopBusyC(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationStopBusyC( arg, event, when, T_SCHEDULER_SET_AFFINITY );
|
|
}
|
|
|
|
static void UpdatePriorityStopBusyC(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationStopBusyC( arg, event, when, T_SCHEDULER_UPDATE_PRIORITY );
|
|
}
|
|
|
|
static void YieldStopBusyC(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationStopBusyC( arg, event, when, T_SCHEDULER_YIELD );
|
|
}
|
|
|
|
static void SuspendA( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
SuspendTask( ctx->worker_id[ WORKER_A ] );
|
|
}
|
|
|
|
static void OperationSuspendA(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when,
|
|
T_scheduler_operation op
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if ( when == T_SCHEDULER_BEFORE && event->operation == op ) {
|
|
const rtems_tcb *worker_a;
|
|
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
ctx->job_context[ 0 ].handler = SuspendA;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
|
|
worker_a = GetThread( ctx->worker_id[ WORKER_A ] );
|
|
|
|
while ( worker_a->Scheduler.state != THREAD_SCHEDULER_BLOCKED ) {
|
|
RTEMS_COMPILER_MEMORY_BARRIER();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void BlockSuspendA(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationSuspendA( arg, event, when, T_SCHEDULER_BLOCK );
|
|
}
|
|
|
|
static void SetAffinitySuspendA(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationSuspendA( arg, event, when, T_SCHEDULER_SET_AFFINITY );
|
|
}
|
|
|
|
static void UpdatePrioritySuspendA(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationSuspendA( arg, event, when, T_SCHEDULER_UPDATE_PRIORITY );
|
|
}
|
|
|
|
static void YieldSuspendA(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
OperationSuspendA( arg, event, when, T_SCHEDULER_YIELD );
|
|
}
|
|
|
|
static void GuideAskForHelp( void *arg )
|
|
{
|
|
Context *ctx;
|
|
Per_CPU_Control *cpu;
|
|
ISR_lock_Context lock_context;
|
|
|
|
ctx = arg;
|
|
cpu = _Per_CPU_Get_by_index( 0 );
|
|
|
|
_ISR_lock_ISR_disable( &lock_context );
|
|
_Per_CPU_Acquire( cpu, &lock_context );
|
|
|
|
ISRLockWaitForOthers( &cpu->Lock, 1 );
|
|
|
|
ctx->job_context[ 0 ].handler = SuspendA;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
ISRLockWaitForOthers( &cpu->Lock, 2 );
|
|
|
|
_Per_CPU_Release( cpu, &lock_context );
|
|
_ISR_lock_ISR_enable( &lock_context );
|
|
}
|
|
|
|
static void InterceptAskForHelp( void *arg )
|
|
{
|
|
Context *ctx;
|
|
Per_CPU_Control *cpu_self;
|
|
|
|
ctx = arg;
|
|
cpu_self = _Per_CPU_Get();
|
|
|
|
if ( rtems_scheduler_get_processor_maximum() > 2 ) {
|
|
ctx->job_context[ 1 ].handler = GuideAskForHelp;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 2 ), &ctx->job[ 1 ] );
|
|
ISRLockWaitForOwned( &cpu_self->Lock );
|
|
} else {
|
|
ISR_lock_Context lock_context;
|
|
Chain_Node *node;
|
|
Thread_Control *thread;
|
|
|
|
_ISR_lock_ISR_disable( &lock_context );
|
|
_Per_CPU_Acquire( cpu_self, &lock_context );
|
|
ctx->job_context[ 0 ].handler = SuspendA;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
ISRLockWaitForOthers( &cpu_self->Lock, 1 );
|
|
|
|
/* See _Thread_Preemption_intervention() */
|
|
node = _Chain_Get_first_unprotected( &cpu_self->Threads_in_need_for_help );
|
|
thread = THREAD_OF_SCHEDULER_HELP_NODE( node );
|
|
T_assert_eq_ptr( thread, GetThread( ctx->worker_id[ WORKER_A ] ) );
|
|
thread->Scheduler.ask_for_help_cpu = NULL;
|
|
|
|
_Per_CPU_Release( cpu_self, &lock_context );
|
|
_ISR_lock_ISR_enable( &lock_context );
|
|
}
|
|
}
|
|
|
|
static void UnblockAskForHelp(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if (
|
|
when == T_SCHEDULER_BEFORE &&
|
|
event->operation == T_SCHEDULER_UNBLOCK
|
|
) {
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
ctx->request.handler = InterceptAskForHelp;
|
|
ctx->request.arg = ctx;
|
|
CallWithinISRSubmit( &ctx->request );
|
|
}
|
|
}
|
|
|
|
static void RaiseWorkerPriorityWithIdleRunner( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
SuspendTask( ctx->runner_id );
|
|
T_scheduler_set_event_handler( UpdatePriorityStopBusyC, ctx );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
|
|
ResumeTask( ctx->runner_id );
|
|
}
|
|
|
|
static void MakeReady( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
MakeBusy( ctx, WORKER_C );
|
|
}
|
|
|
|
static void UpdatePriorityMakeReady(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if (
|
|
when == T_SCHEDULER_BEFORE &&
|
|
event->operation == T_SCHEDULER_UPDATE_PRIORITY
|
|
) {
|
|
Thread_Control *thread;
|
|
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
|
|
thread = GetThread( ctx->worker_id[ WORKER_A ] );
|
|
T_eq_int( thread->Scheduler.state, THREAD_SCHEDULER_SCHEDULED );
|
|
|
|
ctx->job_context[ 0 ].handler = MakeReady;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
|
|
while ( thread->Scheduler.state != THREAD_SCHEDULER_READY ) {
|
|
RTEMS_COMPILER_MEMORY_BARRIER();
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ReadyToScheduled( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
SuspendTask( ctx->runner_id );
|
|
|
|
T_scheduler_set_event_handler( UpdatePriorityMakeReady, ctx );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
|
|
SetPriority( ctx->runner_id, PRIO_VERY_HIGH );
|
|
ResumeTask( ctx->runner_id );
|
|
}
|
|
|
|
static void BlockAndReuseIdle( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
SuspendTask( ctx->runner_id );
|
|
SuspendTask( ctx->worker_id[ WORKER_A ] );
|
|
ResumeTask( ctx->worker_id[ WORKER_A ] );
|
|
SetPriority( ctx->runner_id, PRIO_HIGH );
|
|
ResumeTask( ctx->runner_id );
|
|
}
|
|
|
|
static void Preempt( void *arg )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
MakeBusy( ctx, WORKER_C );
|
|
}
|
|
|
|
static void BlockAndPreempt(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if ( when == T_SCHEDULER_AFTER && event->operation == T_SCHEDULER_BLOCK ) {
|
|
Thread_Control *thread;
|
|
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
|
|
thread = GetThread( ctx->worker_id[ WORKER_A ] );
|
|
T_eq_int( thread->Scheduler.state, THREAD_SCHEDULER_BLOCKED );
|
|
|
|
ctx->job_context[ 0 ].handler = Preempt;
|
|
_Per_CPU_Submit_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
_Per_CPU_Wait_for_job( _Per_CPU_Get_by_index( 1 ), &ctx->job[ 0 ] );
|
|
}
|
|
}
|
|
|
|
static void PrepareOwnerScheduled( Context *ctx )
|
|
{
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
|
|
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
|
|
MakeBusy( ctx, WORKER_C );
|
|
WaitForBusy( ctx, WORKER_C );
|
|
MakeBusy( ctx, WORKER_A );
|
|
}
|
|
|
|
static void CleanupOwnerScheduled( Context *ctx )
|
|
{
|
|
StopBusy( ctx, WORKER_A );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
|
|
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
static void PrepareOwnerBlocked( Context *ctx )
|
|
{
|
|
SetScheduler( ctx->worker_id[ WORKER_A ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SendAndSync( ctx, WORKER_A, EVENT_OBTAIN );
|
|
SendEvents( ctx->worker_id[ WORKER_B ], EVENT_OBTAIN );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
|
|
MakeBusy( ctx, WORKER_C );
|
|
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_LOW );
|
|
MakeBusy( ctx, WORKER_A );
|
|
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_NORMAL );
|
|
}
|
|
|
|
static void CleanupOwnerBlocked( Context *ctx )
|
|
{
|
|
StopBusy( ctx, WORKER_C );
|
|
ResumeTask( ctx->worker_id[ WORKER_A ] );
|
|
StopBusy( ctx, WORKER_A );
|
|
SendAndSync( ctx, WORKER_A, EVENT_RELEASE );
|
|
SetPriority( ctx->worker_id[ WORKER_B ], PRIO_HIGH );
|
|
SendEvents( ctx->worker_id[ WORKER_B ], EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_A ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
static void ReconsiderHelpRequestB(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
(void) when;
|
|
ctx = arg;
|
|
|
|
if ( event->operation == T_SCHEDULER_RECONSIDER_HELP_REQUEST ) {
|
|
Scheduler_SMP_Node *node;
|
|
|
|
node = (Scheduler_SMP_Node *) event->node;
|
|
T_eq_int( node->state, SCHEDULER_SMP_NODE_READY );
|
|
++ctx->counter;
|
|
}
|
|
}
|
|
|
|
static void ReleaseThreadLockB(
|
|
void *arg,
|
|
const T_scheduler_event *event,
|
|
T_scheduler_when when
|
|
)
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = arg;
|
|
|
|
if (
|
|
when == T_SCHEDULER_AFTER &&
|
|
event->operation == T_SCHEDULER_UPDATE_PRIORITY
|
|
) {
|
|
Thread_Control *worker_b;
|
|
|
|
T_scheduler_set_event_handler( ReconsiderHelpRequestB, ctx );
|
|
|
|
worker_b = GetThread( ctx->worker_id[ WORKER_B ] );
|
|
T_eq_int( worker_b->Scheduler.state, THREAD_SCHEDULER_READY );
|
|
|
|
_Thread_State_release_critical( worker_b, &ctx->lock_context );
|
|
}
|
|
}
|
|
|
|
static void Worker( rtems_task_argument arg, WorkerIndex worker )
|
|
{
|
|
Context *ctx;
|
|
|
|
ctx = (Context *) arg;
|
|
|
|
while ( true ) {
|
|
rtems_event_set events;
|
|
|
|
events = ReceiveAnyEvents();
|
|
|
|
if ( ( events & EVENT_SYNC_RUNNER ) != 0 ) {
|
|
SendEvents( ctx->runner_id, EVENT_SYNC_RUNNER );
|
|
}
|
|
|
|
if ( ( events & EVENT_OBTAIN ) != 0 ) {
|
|
ObtainMutex( ctx->mutex_id );
|
|
}
|
|
|
|
if ( ( events & EVENT_RELEASE ) != 0 ) {
|
|
ReleaseMutex( ctx->mutex_id );
|
|
}
|
|
|
|
if ( ( events & EVENT_STICKY_OBTAIN ) != 0 ) {
|
|
ObtainMutex( ctx->sticky_id );
|
|
}
|
|
|
|
if ( ( events & EVENT_STICKY_RELEASE ) != 0 ) {
|
|
ReleaseMutex( ctx->sticky_id );
|
|
}
|
|
|
|
if ( ( events & EVENT_BUSY ) != 0 ) {
|
|
ctx->is_busy[ worker ] = true;
|
|
|
|
while ( ctx->busy[ worker ] ) {
|
|
/* Wait */
|
|
}
|
|
|
|
ctx->is_busy[ worker ] = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void WorkerA( rtems_task_argument arg )
|
|
{
|
|
Worker( arg, WORKER_A );
|
|
}
|
|
|
|
static void WorkerB( rtems_task_argument arg )
|
|
{
|
|
Worker( arg, WORKER_B );
|
|
}
|
|
|
|
static void WorkerC( rtems_task_argument arg )
|
|
{
|
|
Worker( arg, WORKER_C );
|
|
}
|
|
|
|
static void ScoreSchedSmpValSmp_Setup( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
rtems_status_code sc;
|
|
size_t i;
|
|
|
|
ctx->runner_id = rtems_task_self();
|
|
ctx->mutex_id = CreateMutex();
|
|
|
|
for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->job ); ++i ) {
|
|
ctx->job_context[ i ].arg = ctx;
|
|
ctx->job[ i ].context = &ctx->job_context[ i ];
|
|
}
|
|
|
|
sc = rtems_semaphore_create(
|
|
rtems_build_name( 'S', 'T', 'K', 'Y' ),
|
|
1,
|
|
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY |
|
|
RTEMS_MULTIPROCESSOR_RESOURCE_SHARING,
|
|
PRIO_NORMAL,
|
|
&ctx->sticky_id
|
|
);
|
|
T_rsc_success( sc );
|
|
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
|
|
ctx->worker_id[ WORKER_A ] = CreateTask( "WRKA", PRIO_HIGH );
|
|
StartTask( ctx->worker_id[ WORKER_A ], WorkerA, ctx );
|
|
|
|
ctx->worker_id[ WORKER_B ] = CreateTask( "WRKB", PRIO_HIGH );
|
|
StartTask( ctx->worker_id[ WORKER_B ], WorkerB, ctx );
|
|
|
|
ctx->worker_id[ WORKER_C ] = CreateTask( "WRKC", PRIO_HIGH );
|
|
StartTask( ctx->worker_id[ WORKER_C ], WorkerC, ctx );
|
|
}
|
|
|
|
static void ScoreSchedSmpValSmp_Setup_Wrap( void *arg )
|
|
{
|
|
ScoreSchedSmpValSmp_Context *ctx;
|
|
|
|
ctx = arg;
|
|
ScoreSchedSmpValSmp_Setup( ctx );
|
|
}
|
|
|
|
static void ScoreSchedSmpValSmp_Teardown( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
DeleteTask( ctx->worker_id[ WORKER_A ] );
|
|
DeleteTask( ctx->worker_id[ WORKER_B ] );
|
|
DeleteTask( ctx->worker_id[ WORKER_C ] );
|
|
DeleteMutex( ctx->mutex_id );
|
|
DeleteMutex( ctx->sticky_id );
|
|
RestoreRunnerPriority();
|
|
}
|
|
|
|
static void ScoreSchedSmpValSmp_Teardown_Wrap( void *arg )
|
|
{
|
|
ScoreSchedSmpValSmp_Context *ctx;
|
|
|
|
ctx = arg;
|
|
ScoreSchedSmpValSmp_Teardown( ctx );
|
|
}
|
|
|
|
static T_fixture ScoreSchedSmpValSmp_Fixture = {
|
|
.setup = ScoreSchedSmpValSmp_Setup_Wrap,
|
|
.stop = NULL,
|
|
.teardown = ScoreSchedSmpValSmp_Teardown_Wrap,
|
|
.scope = NULL,
|
|
.initial_context = &ScoreSchedSmpValSmp_Instance
|
|
};
|
|
|
|
/**
|
|
* @brief Construct a system state in which a sticky thread is blocked while an
|
|
* idle thread executes on behalf of the thread.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_0( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
|
|
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_STICKY_OBTAIN );
|
|
MakeBusy( ctx, WORKER_A );
|
|
WaitForBusy( ctx, WORKER_A );
|
|
|
|
/*
|
|
* Block the sticky worker A while it uses an idle thread in the home
|
|
* scheduler.
|
|
*/
|
|
CallWithinISR( BlockAndReuseIdle, ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
StopBusy( ctx, WORKER_A );
|
|
SendAndSync( ctx, WORKER_A, EVENT_STICKY_RELEASE );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
|
|
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a thread is preempted while it is
|
|
* blocked.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_1( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
|
|
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
|
|
MakeBusy( ctx, WORKER_A );
|
|
WaitForBusy( ctx, WORKER_A );
|
|
|
|
/*
|
|
* Block worker A and preempt it before the withdraw node operations are
|
|
* performed for worker A.
|
|
*/
|
|
T_scheduler_set_event_handler( BlockAndPreempt, ctx );
|
|
SuspendTask( ctx->worker_id[ WORKER_A ] );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
ResumeTask( ctx->worker_id[ WORKER_A ] );
|
|
StopBusy( ctx, WORKER_C );
|
|
StopBusy( ctx, WORKER_A );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
|
|
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a thread is rescheduled while it
|
|
* is not scheduled on another scheduler.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_2( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
|
|
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_STICKY_OBTAIN );
|
|
MakeBusy( ctx, WORKER_A );
|
|
WaitForBusy( ctx, WORKER_A );
|
|
|
|
/*
|
|
* Reschedule worker A by the home scheduler while worker A is not scheduled
|
|
* on another scheduler.
|
|
*/
|
|
CallWithinISR( ReadyToScheduled, ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
StopBusy( ctx, WORKER_C );
|
|
StopBusy( ctx, WORKER_A );
|
|
SendAndSync( ctx, WORKER_A, EVENT_STICKY_RELEASE );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
|
|
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which an ask for help request is
|
|
* cancelled while it is processed on another processor.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_3( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Unblock worker A. It cannot be scheduled on its home scheduler. Intercept
|
|
* the ask for help request. Block the worker A. This will cancel the ask
|
|
* for help request. Remove the request while the other processor tries to
|
|
* cancel the request.
|
|
*/
|
|
SuspendTask( ctx->worker_id[ WORKER_A ] );
|
|
T_scheduler_set_event_handler( UnblockAskForHelp, ctx );
|
|
ResumeTask( ctx->worker_id[ WORKER_A ] );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
ResumeTask( ctx->worker_id[ WORKER_A ] );
|
|
StopBusy( ctx, WORKER_C );
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a block operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_4( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Block the runner thread while the owner thread of the highest priority
|
|
* ready node is already scheduled.
|
|
*/
|
|
T_scheduler_set_event_handler( BlockStopBusyC, ctx );
|
|
CallWithinISR( Block, ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a block operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_5( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Block the runner thread while the owner thread of the highest priority
|
|
* ready node is blocked.
|
|
*/
|
|
T_scheduler_set_event_handler( BlockSuspendA, ctx );
|
|
CallWithinISR( Block, ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a set affinity
|
|
* operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_6( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*/
|
|
T_scheduler_set_event_handler( SetAffinityStopBusyC, ctx );
|
|
SetSelfAffinityAll();
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a set affinity
|
|
* operation while a sticky node is involved.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_7( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*/
|
|
MakeSticky( ctx );
|
|
T_scheduler_set_event_handler( SetAffinityStopBusyC, ctx );
|
|
SetSelfAffinityAll();
|
|
CleanSticky( ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a set affinity operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_8( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*/
|
|
T_scheduler_set_event_handler( SetAffinitySuspendA, ctx );
|
|
SetSelfAffinityAll();
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a set affinity operation while a
|
|
* sticky node is involved.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_9( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Set the affinity of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*/
|
|
MakeSticky( ctx );
|
|
T_scheduler_set_event_handler( SetAffinitySuspendA, ctx );
|
|
SetSelfAffinityAll();
|
|
CleanSticky( ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a set priority
|
|
* operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_10( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*/
|
|
SetSelfPriority( PRIO_HIGH );
|
|
T_scheduler_set_event_handler( UpdatePriorityStopBusyC, ctx );
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a set priority
|
|
* operation while a sticky node is involved.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_11( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is already scheduled.
|
|
*/
|
|
MakeSticky( ctx );
|
|
CallWithinISR( RaiseWorkerPriorityWithIdleRunner, ctx );
|
|
CleanSticky( ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a set priority operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_12( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Set the priority of the runner thread while the owner thread of the
|
|
* highest priority ready node is blocked.
|
|
*/
|
|
SetSelfPriority( PRIO_HIGH );
|
|
T_scheduler_set_event_handler( UpdatePrioritySuspendA, ctx );
|
|
SetSelfPriority( PRIO_NORMAL );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a yield operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_13( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Yield while the owner thread of the highest priority ready node is already
|
|
* scheduled.
|
|
*/
|
|
T_scheduler_set_event_handler( YieldStopBusyC, ctx );
|
|
Yield();
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is already scheduled during a yield operation
|
|
* while a sticky node is involved.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_14( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerScheduled( ctx );
|
|
|
|
/*
|
|
* Yield while the owner thread of the highest priority ready node is already
|
|
* scheduled.
|
|
*/
|
|
MakeSticky( ctx );
|
|
T_scheduler_set_event_handler( YieldStopBusyC, ctx );
|
|
Yield();
|
|
CleanSticky( ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerScheduled( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a yield operation.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_15( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Yield while the owner thread of the highest priority ready node is
|
|
* blocked.
|
|
*/
|
|
T_scheduler_set_event_handler( YieldSuspendA, ctx );
|
|
Yield();
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Construct a system state in which a scheduler tries to schedule a
|
|
* node those owner thread is blocked during a yield operation while a sticky
|
|
* node is involved.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_16( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
PrepareOwnerBlocked( ctx );
|
|
|
|
/*
|
|
* Yield while the owner thread of the highest priority ready node is
|
|
* blocked.
|
|
*/
|
|
MakeSticky( ctx );
|
|
T_scheduler_set_event_handler( YieldSuspendA, ctx );
|
|
Yield();
|
|
CleanSticky( ctx );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
CleanupOwnerBlocked( ctx );
|
|
}
|
|
|
|
/**
|
|
* @brief Create three worker threads and a mutex. Use the mutex and the
|
|
* worker to check that a not scheduled thread does not get removed from the
|
|
* set of ready threads of a scheduler when a help request is reconsidered.
|
|
*/
|
|
static void ScoreSchedSmpValSmp_Action_17( ScoreSchedSmpValSmp_Context *ctx )
|
|
{
|
|
Thread_Control *worker_b;
|
|
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_B_ID, PRIO_NORMAL );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_B_ID, PRIO_HIGH );
|
|
SendAndSync( ctx, WORKER_B, EVENT_OBTAIN );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_OBTAIN );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_LOW );
|
|
MakeBusy( ctx, WORKER_B );
|
|
WaitForBusy( ctx, WORKER_B );
|
|
MakeBusy( ctx, WORKER_C );
|
|
WaitForBusy( ctx, WORKER_C );
|
|
|
|
/*
|
|
* Prevent that worker B can perform a post-switch cleanup.
|
|
*/
|
|
worker_b = GetThread( ctx->worker_id[ WORKER_B ] );
|
|
_Thread_State_acquire( worker_b, &ctx->lock_context );
|
|
_ISR_lock_ISR_enable( &ctx->lock_context );
|
|
|
|
/*
|
|
* Give worker C a lower priority than worker B. Worker B will try to finish
|
|
* the thread dispatch by doing a post-switch cleanup. The post-switch
|
|
* cleanup cannot progress since the runner owns the thread state lock. Wait
|
|
* until the other processor waits on the thread state lock of worker B.
|
|
*/
|
|
SetPriority( ctx->worker_id[ WORKER_C ], PRIO_LOW );
|
|
TicketLockWaitForOthers( &worker_b->Join_queue.Queue.Lock, 1 );
|
|
|
|
/*
|
|
* Give worker C a higher priority than worker B. Let worker B do its
|
|
* post-switch cleanup which will carry out the reconsider help requests for
|
|
* a not scheduled thread.
|
|
*/
|
|
ctx->counter = 0;
|
|
T_scheduler_set_event_handler( ReleaseThreadLockB, ctx );
|
|
SetPriority( ctx->worker_id[ WORKER_C ], PRIO_HIGH );
|
|
T_scheduler_set_event_handler( NULL, NULL );
|
|
T_eq_u32( ctx->counter, 4 );
|
|
|
|
/*
|
|
* Clean up all used resources.
|
|
*/
|
|
StopBusy( ctx, WORKER_B );
|
|
StopBusy( ctx, WORKER_C );
|
|
SendAndSync( ctx, WORKER_B, EVENT_RELEASE );
|
|
SetPriority( ctx->worker_id[ WORKER_A ], PRIO_HIGH );
|
|
SendEvents( ctx->worker_id[ WORKER_A ], EVENT_RELEASE );
|
|
SetScheduler( ctx->worker_id[ WORKER_B ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
SetScheduler( ctx->worker_id[ WORKER_C ], SCHEDULER_A_ID, PRIO_HIGH );
|
|
}
|
|
|
|
/**
|
|
* @fn void T_case_body_ScoreSchedSmpValSmp( void )
|
|
*/
|
|
T_TEST_CASE_FIXTURE( ScoreSchedSmpValSmp, &ScoreSchedSmpValSmp_Fixture )
|
|
{
|
|
ScoreSchedSmpValSmp_Context *ctx;
|
|
|
|
ctx = T_fixture_context();
|
|
|
|
ScoreSchedSmpValSmp_Action_0( ctx );
|
|
ScoreSchedSmpValSmp_Action_1( ctx );
|
|
ScoreSchedSmpValSmp_Action_2( ctx );
|
|
ScoreSchedSmpValSmp_Action_3( ctx );
|
|
ScoreSchedSmpValSmp_Action_4( ctx );
|
|
ScoreSchedSmpValSmp_Action_5( ctx );
|
|
ScoreSchedSmpValSmp_Action_6( ctx );
|
|
ScoreSchedSmpValSmp_Action_7( ctx );
|
|
ScoreSchedSmpValSmp_Action_8( ctx );
|
|
ScoreSchedSmpValSmp_Action_9( ctx );
|
|
ScoreSchedSmpValSmp_Action_10( ctx );
|
|
ScoreSchedSmpValSmp_Action_11( ctx );
|
|
ScoreSchedSmpValSmp_Action_12( ctx );
|
|
ScoreSchedSmpValSmp_Action_13( ctx );
|
|
ScoreSchedSmpValSmp_Action_14( ctx );
|
|
ScoreSchedSmpValSmp_Action_15( ctx );
|
|
ScoreSchedSmpValSmp_Action_16( ctx );
|
|
ScoreSchedSmpValSmp_Action_17( ctx );
|
|
}
|
|
|
|
/** @} */
|