validation: Improve support library

Update #3716.
This commit is contained in:
Sebastian Huber
2021-12-09 15:49:53 +01:00
parent 6aa0552403
commit 40cc4f5009
18 changed files with 3666 additions and 28 deletions

View File

@@ -20,5 +20,6 @@ links:
uid: validation-1
type: build
use-after:
- validation
- rtemstest
use-before: []

View File

@@ -13,6 +13,13 @@ links: []
source:
- testsuites/validation/tx-call-within-isr.c
- testsuites/validation/tx-interrupt.c
- testsuites/validation/tx-io-relax.c
- testsuites/validation/tx-memory-alloc.c
- testsuites/validation/tx-preemption-intervention.c
- testsuites/validation/tx-support.c
- testsuites/validation/tx-thread-queue.c
- testsuites/validation/tx-timecounter.c
- testsuites/validation/tx-timer-server.c
- testsuites/validation/tx-wrap-thread-queue.c
target: validation
type: build

View File

@@ -0,0 +1,19 @@
SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause
build-type: test-program
cflags: []
copyrights:
- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
cppflags: []
cxxflags: []
enabled-by: true
features: c cprogram
includes: []
ldflags: []
links: []
source:
- testsuites/validation/ts-validation-no-clock-0.c
stlib: []
target: testsuites/validation/ts-validation-no-clock-0.exe
type: build
use-after: []
use-before: []

View File

@@ -1041,7 +1041,7 @@ static void RtemsIntrReqEntryInstall_Setup(
rtems_status_code sc;
ctx->initialized_during_setup = bsp_interrupt_is_initialized();
ctx->test_vector = GetTestableInterruptVector();
ctx->test_vector = GetTestableInterruptVector( NULL );
sc = rtems_interrupt_get_attributes( ctx->test_vector, &ctx->attributes );
T_rsc_success( sc );
}

View File

@@ -1081,7 +1081,7 @@ static void RtemsIntrReqEntryRemove_Setup(
rtems_status_code sc;
ctx->initialized_during_setup = bsp_interrupt_is_initialized();
ctx->test_vector = GetTestableInterruptVector();
ctx->test_vector = GetTestableInterruptVector( NULL );
sc = rtems_interrupt_get_attributes( ctx->test_vector, &ctx->attributes );
T_rsc_success( sc );
}

View File

@@ -505,7 +505,7 @@ static void RtemsIntrReqHandlerIterate_Setup(
rtems_status_code sc;
ctx->initialized_during_setup = bsp_interrupt_is_initialized();
ctx->test_vector = GetTestableInterruptVector();
ctx->test_vector = GetTestableInterruptVector( NULL );
rtems_interrupt_entry_initialize(
&ctx->entry,
EntryRoutine,

View File

@@ -0,0 +1,126 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This header file provides the constants used by the test suite
* configuration.
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifndef _TS_CONFIG_H
#define _TS_CONFIG_H
#include <rtems.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup RTEMSTestSuites
*
* @{
*/
#define TEST_MICROSECONDS_PER_TICK 1000
#define TEST_RUNNER_NAME rtems_build_name( 'R', 'U', 'N', ' ' )
#define TEST_RUNNER_ARGUMENT 123456789
#define TEST_RUNNER_INITIAL_MODES RTEMS_NO_ASR
#define TEST_SCHEDULER_A_NAME rtems_build_name( 'A', ' ', ' ', ' ' )
#define TEST_SCHEDULER_B_NAME rtems_build_name( 'B', ' ', ' ', ' ' )
#define TEST_SCHEDULER_C_NAME rtems_build_name( 'C', ' ', ' ', ' ' )
#define TEST_SCHEDULER_D_NAME rtems_build_name( 'D', ' ', ' ', ' ' )
#if defined( __OPTIMIZE__ ) && !defined( RTEMS_COVERAGE )
#define TEST_BASE_STACK_SIZE RTEMS_MINIMUM_STACK_SIZE
#else
#define TEST_BASE_STACK_SIZE ( 4 * RTEMS_MINIMUM_STACK_SIZE )
#endif
#define TEST_MAXIMUM_TLS_SIZE \
RTEMS_ALIGN_UP( 64, RTEMS_TASK_STORAGE_ALIGNMENT )
#define TEST_MINIMUM_STACK_SIZE \
( TEST_BASE_STACK_SIZE + CPU_STACK_ALIGNMENT )
#define TEST_IDLE_STACK_SIZE \
( TEST_BASE_STACK_SIZE + 2 * CPU_STACK_ALIGNMENT )
#define TEST_INTERRUPT_STACK_SIZE \
( TEST_BASE_STACK_SIZE + 4 * CPU_INTERRUPT_STACK_ALIGNMENT )
#define TEST_MAXIMUM_BARRIERS 7
#define TEST_MAXIMUM_MESSAGE_QUEUES 3
#define TEST_MAXIMUM_PARTITIONS 4
#define TEST_MAXIMUM_PERIODS 2
#define TEST_MAXIMUM_SEMAPHORES 7
#define TEST_MAXIMUM_TASKS 32
#define TEST_MAXIMUM_TIMERS 10
#define TEST_MAXIMUM_USER_EXTENSIONS 5
/*
* Use at least two so that the CPU time budget decrement in
* _Scheduler_default_Tick() does not always result in a zero.
*/
#define TEST_TICKS_PER_TIMESLICE 2
void *test_task_stack_allocate( size_t size );
void test_task_stack_deallocate( void *stack );
void *test_idle_task_stack_allocate( uint32_t cpu_index, size_t size );
extern rtems_task_argument test_runner_argument;
extern rtems_task_priority test_runner_initial_priority;
extern rtems_mode test_runner_initial_modes;
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _TS_CONFIG_H */

View File

@@ -45,6 +45,30 @@
#include <bsp/irq-generic.h>
static bool HasRequiredAttributes(
const rtems_interrupt_attributes *required,
const rtems_interrupt_attributes *actual
)
{
if ( required == NULL ) {
return true;
}
if ( required->can_get_affinity && !actual->can_get_affinity ) {
return false;
}
if ( required->can_raise && !actual->can_raise ) {
return false;
}
if ( required->can_raise_on && !actual->can_raise_on ) {
return false;
}
return true;
}
rtems_vector_number GetValidInterruptVectorNumber(
const rtems_interrupt_attributes *required
)
@@ -57,11 +81,7 @@ rtems_vector_number GetValidInterruptVectorNumber(
sc = rtems_interrupt_get_attributes( vector, &attr );
if (
sc == RTEMS_SUCCESSFUL &&
( required == NULL ||
!required->can_get_affinity || attr.can_get_affinity )
) {
if ( sc == RTEMS_SUCCESSFUL && HasRequiredAttributes( required, &attr ) ) {
break;
}
}
@@ -69,7 +89,9 @@ rtems_vector_number GetValidInterruptVectorNumber(
return vector;
}
rtems_vector_number GetTestableInterruptVector( void )
rtems_vector_number GetTestableInterruptVector(
const rtems_interrupt_attributes *required
)
{
rtems_vector_number vector;
@@ -87,6 +109,10 @@ rtems_vector_number GetTestableInterruptVector( void )
continue;
}
if ( !HasRequiredAttributes( required, &attr ) ) {
continue;
}
if ( HasInterruptVectorEntriesInstalled( vector ) ) {
continue;
}

View File

@@ -0,0 +1,67 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the implementation of SetIORelaxHandler().
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
void __real__IO_Relax( void );
void __wrap__IO_Relax( void );
static void ( *io_relax_handler )( void * );
static void *io_relax_arg;
void __wrap__IO_Relax( void )
{
void ( *handler )( void * );
handler = io_relax_handler;
if ( handler != NULL ) {
( *handler )( io_relax_arg );
}
__real__IO_Relax();
}
void SetIORelaxHandler( void ( *handler )( void * ), void *arg )
{
io_relax_handler = handler;
io_relax_arg = arg;
}

View File

@@ -0,0 +1,79 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the implementation of
* MemoryAllocationFailWhen() and __wrap_rtems_malloc().
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
void *__real_rtems_malloc( size_t );
void *__wrap_rtems_malloc( size_t );
static uint32_t fail_when_counter;
void MemoryAllocationFailWhen( uint32_t counter )
{
fail_when_counter = counter;
}
static bool IsFail( void )
{
uint32_t counter;
counter = fail_when_counter;
if ( counter == 1 ) {
fail_when_counter = 0;
return true;
}
if ( counter > 1 ) {
fail_when_counter = counter - 1;
}
return false;
}
void *__wrap_rtems_malloc( size_t size )
{
if ( IsFail() ) {
return NULL;
}
return __real_rtems_malloc( size );
}

View File

@@ -0,0 +1,155 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the implementation of
* SetPreemptionIntervention().
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
#include <rtems/sysinit.h>
#include <rtems/score/chainimpl.h>
#include <rtems/score/percpu.h>
#include <rtems/score/threadimpl.h>
#include <rtems/test.h>
#if defined(RTEMS_SMP)
typedef struct {
void ( *handler )( void * );
void *arg;
Scheduler_Context scheduler_context;
Scheduler_Node scheduler_node;
Thread_Control thread;
} PreemptionInterventionContext;
static PreemptionInterventionContext preemption_intervention_instance;
static bool PreemptionInterventionAskForHelp(
const Scheduler_Control *scheduler,
Thread_Control *thread,
Scheduler_Node *node
)
{
PreemptionInterventionContext *ctx;
void ( *handler )( void * );
void *arg;
(void) scheduler;
(void) thread;
(void) node;
ctx = &preemption_intervention_instance;
handler = ctx->handler;
arg = ctx->arg;
ctx->handler = NULL;
ctx->arg = NULL;
( *handler )( arg );
return true;
}
static const Scheduler_Control preemption_intervention_scheduler = {
.context = &preemption_intervention_instance.scheduler_context,
.Operations = {
.ask_for_help = PreemptionInterventionAskForHelp
}
};
static void PreemptionInterventionInitialize( void )
{
PreemptionInterventionContext *ctx;
ctx = &preemption_intervention_instance;
_Chain_Initialize_node( &ctx->thread.Scheduler.Help_node );
_Thread_queue_Initialize(
&ctx->thread.Join_queue,
"Preemption Intervention"
);
_ISR_lock_Initialize(
&ctx->scheduler_context.Lock,
"Preemption Intervention"
);
_Scheduler_Node_do_initialize(
&preemption_intervention_scheduler,
&ctx->scheduler_node,
&ctx->thread,
0
);
_Chain_Initialize_one(
&ctx->thread.Scheduler.Scheduler_nodes,
&ctx->scheduler_node.Thread.Scheduler_node.Chain
);
}
RTEMS_SYSINIT_ITEM(
PreemptionInterventionInitialize,
RTEMS_SYSINIT_DEVICE_DRIVERS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
#endif
void SetPreemptionIntervention(
struct Per_CPU_Control *cpu,
void ( *handler )( void * ),
void *arg
)
{
#if defined(RTEMS_SMP)
PreemptionInterventionContext *ctx;
rtems_interrupt_level level;
ISR_lock_Context lock_context;
ctx = &preemption_intervention_instance;
T_quiet_assert_null( ctx->handler );
ctx->handler = handler;
ctx->arg = arg;
rtems_interrupt_local_disable( level );
_Per_CPU_Acquire( cpu, &lock_context );
_Chain_Append_unprotected(
&cpu->Threads_in_need_for_help,
&ctx->thread.Scheduler.Help_node
);
_Per_CPU_Release( cpu, &lock_context );
rtems_interrupt_local_enable( level );
#else
(void) cpu;
(void) handler;
(void) arg;
#endif
}

View File

@@ -5,11 +5,8 @@
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the definition of DeleteTask(),
* DoCreateTask(), GetMode(), GetPriority(), GetSelfPriority(),
* ReceiveAnyEvents(), RestoreRunnerASR(), RestoreRunnerMode(),
* RestoreRunnerPriority(), SendEvents(), SetMode(), SetSelfPriority(),
* SetPriority(), and StartTask().
* @brief This source file contains the implementation of support functions for
* the validation test cases.
*/
/*
@@ -42,8 +39,16 @@
#endif
#include "tx-support.h"
#include "ts-config.h"
#include <rtems/test.h>
#include <rtems/score/percpu.h>
#include <rtems/score/smpimpl.h>
#include <rtems/score/threaddispatch.h>
#include <rtems/score/threadimpl.h>
#include <rtems/rtems/semimpl.h>
#include <string.h>
rtems_id DoCreateTask( rtems_name name, rtems_task_priority priority )
{
@@ -53,7 +58,7 @@ rtems_id DoCreateTask( rtems_name name, rtems_task_priority priority )
sc = rtems_task_create(
name,
priority,
RTEMS_MINIMUM_STACK_SIZE,
TEST_MINIMUM_STACK_SIZE,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&id
@@ -77,33 +82,115 @@ void DeleteTask( rtems_id id )
rtems_status_code sc;
sc = rtems_task_delete( id );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
}
}
rtems_event_set ReceiveAnyEvents( void )
void SuspendTask( rtems_id id )
{
rtems_status_code sc;
sc = rtems_task_suspend( id );
T_quiet_rsc_success( sc );
}
void SuspendSelf( void )
{
SuspendTask( RTEMS_SELF );
}
void ResumeTask( rtems_id id )
{
rtems_status_code sc;
sc = rtems_task_resume( id );
T_quiet_rsc_success( sc );
}
bool IsTaskSuspended( rtems_id id )
{
rtems_status_code sc;
sc = rtems_task_is_suspended( id );
T_quiet_true( sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED );
return sc == RTEMS_ALREADY_SUSPENDED;
}
rtems_event_set QueryPendingEvents( void )
{
rtems_status_code sc;
rtems_event_set events;
events = 0;
sc = rtems_event_receive(
RTEMS_ALL_EVENTS,
RTEMS_EVENT_ANY | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
RTEMS_PENDING_EVENTS,
RTEMS_EVENT_ALL | RTEMS_NO_WAIT,
0,
&events
);
T_rsc_success( sc );
T_quiet_rsc_success( sc );
return events;
}
rtems_event_set PollAnyEvents( void )
{
rtems_event_set events;
events = 0;
(void) rtems_event_receive(
RTEMS_ALL_EVENTS,
RTEMS_EVENT_ANY | RTEMS_NO_WAIT,
0,
&events
);
return events;
}
rtems_event_set ReceiveAnyEvents( void )
{
return ReceiveAnyEventsTimed( RTEMS_NO_TIMEOUT );
}
rtems_event_set ReceiveAnyEventsTimed( rtems_interval ticks )
{
rtems_event_set events;
events = 0;
(void) rtems_event_receive(
RTEMS_ALL_EVENTS,
RTEMS_EVENT_ANY | RTEMS_WAIT,
ticks,
&events
);
return events;
}
void ReceiveAllEvents( rtems_event_set events )
{
rtems_status_code sc;
rtems_event_set received;
received = 0;
sc = rtems_event_receive(
events,
RTEMS_EVENT_ALL | RTEMS_WAIT,
RTEMS_NO_TIMEOUT,
&received
);
T_quiet_rsc_success( sc );
T_quiet_eq_u32( received, events );
}
void SendEvents( rtems_id id, rtems_event_set events )
{
rtems_status_code sc;
sc = rtems_event_send( id, events );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
}
rtems_mode GetMode( void )
@@ -117,7 +204,7 @@ rtems_mode SetMode( rtems_mode set, rtems_mode mask )
rtems_mode previous;
sc = rtems_task_mode( set, mask, &previous );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
return previous;
}
@@ -127,13 +214,32 @@ rtems_task_priority GetPriority( rtems_id id )
return SetPriority( id, RTEMS_CURRENT_PRIORITY );
}
rtems_task_priority GetPriorityByScheduler(
rtems_id task_id,
rtems_id scheduler_id
)
{
rtems_status_code sc;
rtems_task_priority priority;
priority = PRIO_INVALID;
sc = rtems_task_get_priority( task_id, scheduler_id, &priority );
if ( sc != RTEMS_SUCCESSFUL ) {
return PRIO_INVALID;
}
return priority;
}
rtems_task_priority SetPriority( rtems_id id, rtems_task_priority priority )
{
rtems_status_code sc;
rtems_task_priority previous;
previous = PRIO_INVALID;
sc = rtems_task_set_priority( id, priority, &previous );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
return previous;
}
@@ -148,12 +254,293 @@ rtems_task_priority SetSelfPriority( rtems_task_priority priority )
return SetPriority( RTEMS_SELF, priority );
}
rtems_task_priority SetSelfPriorityNoYield( rtems_task_priority priority )
{
rtems_status_code sc;
rtems_id id;
/*
* If the priority is lowered, then this sequence ensures that we do not
* carry out an implicit yield.
*/
sc = rtems_semaphore_create(
rtems_build_name( 'T', 'E', 'M', 'P' ),
0,
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_PRIORITY_CEILING,
1,
&id
);
T_quiet_rsc_success( sc );
priority = SetSelfPriority( priority );
ReleaseMutex( id );
DeleteMutex( id );
return priority;
}
rtems_id GetScheduler( rtems_id id )
{
rtems_status_code sc;
rtems_id scheduler_id;
scheduler_id = 0xffffffff;
sc = rtems_task_get_scheduler( id, &scheduler_id );
T_quiet_rsc_success( sc );
return scheduler_id;
}
rtems_id GetSelfScheduler( void )
{
return GetScheduler( RTEMS_SELF );
}
void SetScheduler(
rtems_id task_id,
rtems_id scheduler_id,
rtems_task_priority priority
)
{
rtems_status_code sc;
sc = rtems_task_set_scheduler( task_id, scheduler_id, priority );
T_quiet_rsc_success( sc );
}
void SetSelfScheduler( rtems_id scheduler_id, rtems_task_priority priority )
{
SetScheduler( RTEMS_SELF, scheduler_id, priority );
}
void GetAffinity( rtems_id id, cpu_set_t *set )
{
rtems_status_code sc;
CPU_ZERO( set );
sc = rtems_task_get_affinity( id, sizeof( *set ), set );
T_quiet_rsc_success( sc );
}
void GetSelfAffinity( cpu_set_t *set )
{
GetAffinity( RTEMS_SELF, set );
}
void SetAffinity( rtems_id id, const cpu_set_t *set )
{
rtems_status_code sc;
sc = rtems_task_set_affinity( id, sizeof( *set ), set );
T_quiet_rsc_success( sc );
}
void SetSelfAffinity( const cpu_set_t *set )
{
SetAffinity( RTEMS_SELF, set );
}
void SetAffinityOne( rtems_id id, uint32_t cpu_index )
{
cpu_set_t set;
CPU_ZERO( &set );
CPU_SET( (int) cpu_index, &set );
SetAffinity( id, &set );
}
void SetSelfAffinityOne( uint32_t cpu_index )
{
SetAffinityOne( RTEMS_SELF, cpu_index );
}
void SetAffinityAll( rtems_id id )
{
cpu_set_t set;
CPU_FILL( &set );
SetAffinity( id, &set );
}
void SetSelfAffinityAll( void )
{
SetAffinityAll( RTEMS_SELF );
}
void Yield( void )
{
rtems_status_code sc;
sc = rtems_task_wake_after( RTEMS_YIELD_PROCESSOR );
T_quiet_rsc_success( sc );
}
void YieldTask( rtems_id id )
{
Thread_Control *the_thread;
ISR_lock_Context lock_context;
Per_CPU_Control *cpu_self;
the_thread = _Thread_Get( id, &lock_context );
if ( the_thread == NULL ) {
return;
}
cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
_ISR_lock_ISR_enable( &lock_context);
_Thread_Yield( the_thread );
_Thread_Dispatch_direct( cpu_self );
}
void AddProcessor( rtems_id scheduler_id, uint32_t cpu_index )
{
rtems_status_code sc;
sc = rtems_scheduler_add_processor( scheduler_id, cpu_index );
T_quiet_rsc_success( sc );
}
void RemoveProcessor( rtems_id scheduler_id, uint32_t cpu_index )
{
rtems_status_code sc;
sc = rtems_scheduler_remove_processor( scheduler_id, cpu_index );
T_quiet_rsc_success( sc );
}
rtems_id CreateMutex( void )
{
rtems_status_code sc;
rtems_id id;
id = INVALID_ID;
sc = rtems_semaphore_create(
rtems_build_name( 'M', 'U', 'T', 'X' ),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY | RTEMS_INHERIT_PRIORITY,
0,
&id
);
T_rsc_success( sc );
return id;
}
rtems_id CreateMutexNoProtocol( void )
{
rtems_status_code sc;
rtems_id id;
id = INVALID_ID;
sc = rtems_semaphore_create(
rtems_build_name( 'M', 'U', 'T', 'X' ),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_PRIORITY,
0,
&id
);
T_rsc_success( sc );
return id;
}
rtems_id CreateMutexFIFO( void )
{
rtems_status_code sc;
rtems_id id;
id = INVALID_ID;
sc = rtems_semaphore_create(
rtems_build_name( 'M', 'U', 'T', 'X' ),
1,
RTEMS_BINARY_SEMAPHORE | RTEMS_FIFO,
0,
&id
);
T_rsc_success( sc );
return id;
}
void DeleteMutex( rtems_id id )
{
if ( id != INVALID_ID ) {
rtems_status_code sc;
sc = rtems_semaphore_delete( id );
T_rsc_success( sc );
}
}
bool IsMutexOwner( rtems_id id )
{
Semaphore_Control *the_semaphore;
Thread_queue_Context queue_context;
the_semaphore = _Semaphore_Get( id, &queue_context );
if ( the_semaphore == NULL ) {
return false;
}
_ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
return the_semaphore->Core_control.Wait_queue.Queue.owner ==
_Thread_Get_executing();
}
void ObtainMutex( rtems_id id )
{
rtems_status_code sc;
sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
T_rsc_success( sc );
}
void ObtainMutexTimed( rtems_id id, rtems_interval ticks )
{
rtems_status_code sc;
sc = rtems_semaphore_obtain( id, RTEMS_WAIT, ticks );
T_rsc_success( sc );
}
void ObtainMutexDeadlock( rtems_id id )
{
rtems_status_code sc;
sc = rtems_semaphore_obtain( id, RTEMS_WAIT, RTEMS_NO_TIMEOUT );
T_rsc( sc, RTEMS_INCORRECT_STATE );
}
void ReleaseMutex( rtems_id id )
{
rtems_status_code sc;
sc = rtems_semaphore_release( id );
T_rsc_success( sc );
}
Thread_queue_Queue *GetMutexThreadQueue( rtems_id id )
{
Semaphore_Control *the_semaphore;
Thread_queue_Context queue_context;
the_semaphore = _Semaphore_Get( id, &queue_context );
if ( the_semaphore == NULL ) {
return NULL;
}
_ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
return &the_semaphore->Core_control.Wait_queue.Queue;
}
void RestoreRunnerASR( void )
{
rtems_status_code sc;
sc = rtems_signal_catch( NULL, RTEMS_DEFAULT_MODES );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
}
void RestoreRunnerMode( void )
@@ -162,10 +549,480 @@ void RestoreRunnerMode( void )
rtems_mode mode;
sc = rtems_task_mode( RTEMS_DEFAULT_MODES, RTEMS_ALL_MODE_MASKS, &mode );
T_rsc_success( sc );
T_quiet_rsc_success( sc );
}
void RestoreRunnerPriority( void )
{
SetSelfPriority( PRIO_ULTRA_HIGH );
SetSelfPriority( 1 );
}
Thread_Control *GetThread( rtems_id id )
{
Thread_Control *the_thread;
ISR_lock_Context lock_context;
the_thread = _Thread_Get( id, &lock_context );
if ( the_thread == NULL ) {
return NULL;
}
_ISR_lock_ISR_enable( &lock_context);
return the_thread;
}
Thread_Control *GetExecuting( void )
{
return _Thread_Get_executing();
}
void KillZombies( void )
{
_RTEMS_Lock_allocator();
_Thread_Kill_zombies();
_RTEMS_Unlock_allocator();
}
void WaitForExecutionStop( rtems_id task_id )
{
#if defined( RTEMS_SMP )
Thread_Control *the_thread;
the_thread = GetThread( task_id );
T_assert_not_null( the_thread );
while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
/* Wait */
}
#else
(void) task_id;
#endif
}
void WaitForIntendToBlock( rtems_id task_id )
{
#if defined( RTEMS_SMP )
Thread_Control *the_thread;
Thread_Wait_flags intend_to_block;
the_thread = GetThread( task_id );
T_assert_not_null( the_thread );
intend_to_block = THREAD_WAIT_CLASS_OBJECT |
THREAD_WAIT_STATE_INTEND_TO_BLOCK;
while ( _Thread_Wait_flags_get_acquire( the_thread ) != intend_to_block ) {
/* Wait */
}
#else
(void) task_id;
#endif
}
void WaitForHeir( uint32_t cpu_index, rtems_id task_id )
{
Per_CPU_Control *cpu;
cpu = _Per_CPU_Get_by_index( cpu_index );
while ( cpu->heir->Object.id != task_id ) {
RTEMS_COMPILER_MEMORY_BARRIER();
}
}
void WaitForNextTask( uint32_t cpu_index, rtems_id task_id )
{
Per_CPU_Control *cpu;
cpu = _Per_CPU_Get_by_index( cpu_index );
while ( cpu->heir->Object.id == task_id ) {
RTEMS_COMPILER_MEMORY_BARRIER();
}
while ( cpu->thread_dispatch_disable_level != 0 ) {
RTEMS_COMPILER_MEMORY_BARRIER();
}
}
void GetTaskTimerInfo( rtems_id id, TaskTimerInfo *info )
{
GetTaskTimerInfoByThread( GetThread( id ), info );
}
void GetTaskTimerInfoByThread(
struct _Thread_Control *thread,
TaskTimerInfo *info
)
{
info->expire_ticks = 0;
info->expire_timespec.tv_sec = -1;
info->expire_timespec.tv_nsec = -1;
if ( thread != NULL ) {
ISR_lock_Context lock_context;
ISR_lock_Context lock_context_2;
Per_CPU_Control *cpu;
_ISR_lock_ISR_disable_and_acquire( &thread->Timer.Lock, &lock_context );
info->expire_ticks = thread->Timer.Watchdog.expire;
#if defined( RTEMS_SMP )
cpu = thread->Timer.Watchdog.cpu;
#else
cpu = _Per_CPU_Get();
#endif
_Watchdog_Per_CPU_acquire_critical( cpu, &lock_context_2 );
if ( _Watchdog_Is_scheduled( &thread->Timer.Watchdog ) ) {
const Watchdog_Header *hdr;
hdr = thread->Timer.header;
if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ] ) {
info->state = TASK_TIMER_TICKS;
} else {
_Watchdog_Ticks_to_timespec(
info->expire_ticks,
&info->expire_timespec
);
if ( hdr == &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ] ) {
info->state = TASK_TIMER_REALTIME;
} else {
T_quiet_eq_ptr(
hdr,
&cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ]
);
info->state = TASK_TIMER_MONOTONIC;
}
}
} else {
info->state = TASK_TIMER_INACTIVE;
}
_Watchdog_Per_CPU_release_critical( cpu, &lock_context_2 );
_ISR_lock_Release_and_ISR_enable( &thread->Timer.Lock, &lock_context );
} else {
info->state = TASK_TIMER_INVALID;
}
}
#if defined( RTEMS_SMP )
static void DoWatchdogTick( void *arg )
{
(void) arg;
_Watchdog_Tick( _Per_CPU_Get() );
}
#endif
void ClockTick( void )
{
Per_CPU_Control *cpu_self;
cpu_self = _Thread_Dispatch_disable();
#if defined( RTEMS_SMP )
DoWatchdogTick( NULL );
_SMP_Othercast_action( DoWatchdogTick, NULL );
#else
_Watchdog_Tick( cpu_self );
#endif
_Thread_Dispatch_enable( cpu_self );
}
static void FinalWatchdogTick( Per_CPU_Control *cpu )
{
ISR_lock_Context lock_context;
Watchdog_Header *header;
Watchdog_Control *first;
_ISR_lock_ISR_disable_and_acquire( &cpu->Watchdog.Lock, &lock_context );
header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_TICKS ];
first = _Watchdog_Header_first( header );
if ( first != NULL ) {
_Watchdog_Tickle(
header,
first,
UINT64_MAX,
&cpu->Watchdog.Lock,
&lock_context
);
}
header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_MONOTONIC ];
first = _Watchdog_Header_first( header );
if ( first != NULL ) {
_Watchdog_Tickle(
header,
first,
UINT64_MAX,
&cpu->Watchdog.Lock,
&lock_context
);
}
header = &cpu->Watchdog.Header[ PER_CPU_WATCHDOG_REALTIME ];
first = _Watchdog_Header_first( header );
if ( first != NULL ) {
_Watchdog_Tickle(
header,
first,
UINT64_MAX,
&cpu->Watchdog.Lock,
&lock_context
);
}
_ISR_lock_Release_and_ISR_enable( &cpu->Watchdog.Lock, &lock_context );
}
#if defined( RTEMS_SMP )
static void DoFinalWatchdogTick( void *arg )
{
(void) arg;
FinalWatchdogTick( _Per_CPU_Get() );
}
#endif
void FinalClockTick( void )
{
Per_CPU_Control *cpu_self;
cpu_self = _Thread_Dispatch_disable();
#if defined( RTEMS_SMP )
DoFinalWatchdogTick( NULL );
_SMP_Othercast_action( DoFinalWatchdogTick, NULL );
#else
FinalWatchdogTick( cpu_self );
#endif
_Thread_Dispatch_enable( cpu_self );
}
static FatalHandler fatal_handler;
static void *fatal_arg;
void FatalInitialExtension(
rtems_fatal_source source,
bool always_set_to_false,
rtems_fatal_code code
)
{
FatalHandler fatal;
T_quiet_false( always_set_to_false );
fatal = fatal_handler;
if ( fatal != NULL ) {
( *fatal )( source, code, fatal_arg );
}
}
void SetFatalHandler( FatalHandler fatal, void *arg )
{
fatal_handler = fatal;
fatal_arg = arg;
}
static rtems_id task_switch_id;
static rtems_task_switch_extension task_switch_extension;
static void TaskSwitchExtension( rtems_tcb *executing, rtems_tcb *heir )
{
( *task_switch_extension )( executing, heir );
}
void SetTaskSwitchExtension( rtems_task_switch_extension task_switch )
{
rtems_task_switch_extension last;
rtems_status_code sc;
last = task_switch_extension;
if ( task_switch == NULL ) {
if ( last != NULL ) {
sc = rtems_extension_delete( task_switch_id );
T_quiet_rsc_success( sc );
task_switch_extension = NULL;
}
} else {
task_switch_extension = task_switch;
if ( last == NULL ) {
rtems_extensions_table table = {
.thread_switch = TaskSwitchExtension
};
sc = rtems_extension_create(
rtems_build_name( 'T', 'S', 'W', 'I' ),
&table,
&task_switch_id
);
T_quiet_rsc_success( sc );
}
}
}
void ClearExtensionCalls( ExtensionCalls *calls )
{
memset( calls, 0, sizeof( *calls ) );
}
void CopyExtensionCalls( const ExtensionCalls *from, ExtensionCalls *to )
{
memcpy( to, from, sizeof( *to ) );
}
#if defined(RTEMS_SMP)
static volatile bool delay_thread_dispatch;
static void DelayThreadDispatchHandler( void *arg )
{
(void) arg;
while ( delay_thread_dispatch ) {
/* Wait */
}
}
static const Per_CPU_Job_context delay_thread_dispatch_context = {
.handler = DelayThreadDispatchHandler
};
static Per_CPU_Job delay_thread_dispatch_job = {
.context = &delay_thread_dispatch_context
};
#endif
void StartDelayThreadDispatch( uint32_t cpu_index )
{
#if defined(RTEMS_SMP)
if ( rtems_configuration_get_maximum_processors() > cpu_index ) {
delay_thread_dispatch = true;
_Per_CPU_Submit_job(
_Per_CPU_Get_by_index( cpu_index ),
&delay_thread_dispatch_job
);
}
#endif
}
void StopDelayThreadDispatch( uint32_t cpu_index )
{
#if defined(RTEMS_SMP)
if ( rtems_configuration_get_maximum_processors() > cpu_index ) {
Per_CPU_Control *cpu_self;
cpu_self = _Thread_Dispatch_disable();
delay_thread_dispatch = false;
_Per_CPU_Wait_for_job(
_Per_CPU_Get_by_index( cpu_index ),
&delay_thread_dispatch_job
);
_Thread_Dispatch_enable( cpu_self );
}
#endif
}
bool AreInterruptsEnabled( void )
{
return _ISR_Get_level() == 0;
}
static bool IsWhiteSpace( char c )
{
return c == ' ' || c == '\t';
}
bool IsWhiteSpaceOnly( const char *s )
{
char c;
while ( ( c = *s ) != '\0' ) {
if ( !IsWhiteSpace( c ) ) {
return false;
}
++s;
}
return true;
}
static const char *EatWhiteSpace( const char *s )
{
char c;
while ( ( c = *s ) != '\0' ) {
if ( !IsWhiteSpace( c ) ) {
break;
}
++s;
}
return s;
}
bool IsEqualIgnoreWhiteSpace( const char *a, const char *b )
{
while ( true ) {
a = EatWhiteSpace( a );
b = EatWhiteSpace( b );
if ( *a != *b ) {
return false;
}
if ( *a == '\0' ) {
return true;
}
++a;
++b;
}
return true;
}
#if defined(RTEMS_SMP)
bool TicketLockIsAvailable( const SMP_ticket_lock_Control *lock )
{
unsigned int now_serving;
unsigned int next_ticket;
now_serving = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED );
next_ticket = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED );
return now_serving == next_ticket;
}
void TicketLockWaitForOwned( const SMP_ticket_lock_Control *lock )
{
while ( TicketLockIsAvailable( lock ) ) {
/* Wait */
}
}
void TicketLockWaitForOthers(
const SMP_ticket_lock_Control *lock,
unsigned int others
)
{
unsigned int expected;
unsigned int actual;
expected = _Atomic_Load_uint( &lock->now_serving, ATOMIC_ORDER_RELAXED );
expected += others + 1;
do {
actual = _Atomic_Load_uint( &lock->next_ticket, ATOMIC_ORDER_RELAXED );
} while ( expected != actual );
}
#endif

View File

@@ -40,6 +40,7 @@
#include <rtems.h>
#include <rtems/irq-extension.h>
#include <rtems/score/atomic.h>
#include <rtems/score/threadq.h>
#ifdef __cplusplus
extern "C" {
@@ -53,6 +54,7 @@ extern "C" {
typedef enum {
PRIO_PSEUDO_ISR,
PRIO_VERY_ULTRA_HIGH,
PRIO_ULTRA_HIGH,
PRIO_VERY_HIGH,
PRIO_HIGH,
@@ -62,25 +64,85 @@ typedef enum {
PRIO_ULTRA_LOW
} Priority;
/**
* @brief This constants represents the default priority of the runner task.
*/
#define PRIO_DEFAULT 1
/**
* @brief This constants represents an invalid RTEMS task priority value.
*
* It should be an invalid priority value which is not equal to
* RTEMS_CURRENT_PRIORITY and RTEMS_TIMER_SERVER_DEFAULT_PRIORITY.
*/
#define PRIO_INVALID 0xfffffffe
/**
* @brief This constants represents a priority which is close to the priority
* of the idle thread.
*
* It may be used for the runner thread together with PRIO_FLEXIBLE for worker
* threads.
*/
#define PRIO_NEARLY_IDLE 126
/**
* @brief This constants represents a priority with a wider range of higher and
* lower priorities around it.
*
* It may be used for the worker threads together with PRIO_NEARLY_IDLE for the
* runner thread.
*/
#define PRIO_FLEXIBLE 64
/**
* @brief This constants represents an invalid RTEMS object identifier.
*/
#define INVALID_ID 0xfffffffd
/**
* @brief This constants represents an object name for tests.
*/
#define OBJECT_NAME rtems_build_name( 'T', 'E', 'S', 'T' )
#define CreateTask( name, priority ) \
DoCreateTask( \
rtems_build_name( name[ 0 ], name[ 1 ], name[ 2 ], name[ 3 ] ), \
priority \
)
#define SCHEDULER_A_ID 0xf010001
#define SCHEDULER_B_ID 0xf010002
#define SCHEDULER_C_ID 0xf010003
#define SCHEDULER_D_ID 0xf010004
rtems_id DoCreateTask( rtems_name name, rtems_task_priority priority );
void StartTask( rtems_id id, rtems_task_entry entry, void *arg );
void DeleteTask( rtems_id id );
void SuspendTask( rtems_id id );
void SuspendSelf( void );
void ResumeTask( rtems_id id );
bool IsTaskSuspended( rtems_id id );
rtems_event_set QueryPendingEvents( void );
rtems_event_set PollAnyEvents( void );
rtems_event_set ReceiveAnyEvents( void );
rtems_event_set ReceiveAnyEventsTimed( rtems_interval ticks );
void ReceiveAllEvents( rtems_event_set events );
void SendEvents( rtems_id id, rtems_event_set events );
rtems_mode GetMode( void );
@@ -89,18 +151,230 @@ rtems_mode SetMode( rtems_mode set, rtems_mode mask );
rtems_task_priority GetPriority( rtems_id id );
rtems_task_priority GetPriorityByScheduler(
rtems_id task_id,
rtems_id scheduler_id
);
rtems_task_priority SetPriority( rtems_id id, rtems_task_priority priority );
rtems_task_priority GetSelfPriority( void );
rtems_task_priority SetSelfPriority( rtems_task_priority priority );
rtems_task_priority SetSelfPriorityNoYield( rtems_task_priority priority );
rtems_id GetScheduler( rtems_id id );
rtems_id GetSelfScheduler( void );
void SetScheduler(
rtems_id task_id,
rtems_id scheduler_id,
rtems_task_priority priority
);
void SetSelfScheduler( rtems_id scheduler_id, rtems_task_priority priority );
void GetAffinity( rtems_id id, cpu_set_t *set );
void GetSelfAffinity( cpu_set_t *set );
void SetAffinity( rtems_id id, const cpu_set_t *set );
void SetSelfAffinity( const cpu_set_t *set );
void SetAffinityOne( rtems_id id, uint32_t cpu_index );
void SetSelfAffinityOne( uint32_t cpu_index );
void SetAffinityAll( rtems_id id );
void SetSelfAffinityAll( void );
void Yield( void );
void YieldTask( rtems_id id );
void AddProcessor( rtems_id scheduler_id, uint32_t cpu_index );
void RemoveProcessor( rtems_id scheduler_id, uint32_t cpu_index );
rtems_id CreateMutex( void );
rtems_id CreateMutexNoProtocol( void );
rtems_id CreateMutexFIFO( void );
bool IsMutexOwner( rtems_id id );
void DeleteMutex( rtems_id id );
void ObtainMutex( rtems_id id );
void ObtainMutexTimed( rtems_id id, rtems_interval ticks );
void ObtainMutexDeadlock( rtems_id id );
void ReleaseMutex( rtems_id id );
struct Thread_queue_Queue;
struct Thread_queue_Queue *GetMutexThreadQueue( rtems_id id );
void RestoreRunnerASR( void );
void RestoreRunnerMode( void );
void RestoreRunnerPriority( void );
struct _Thread_Control;
struct _Thread_Control *GetThread( rtems_id id );
struct _Thread_Control *GetExecuting( void );
void KillZombies( void );
void WaitForExecutionStop( rtems_id task_id );
void WaitForIntendToBlock( rtems_id task_id );
void WaitForHeir( uint32_t cpu_index, rtems_id task_id );
void WaitForNextTask( uint32_t cpu_index, rtems_id task_id );
typedef enum {
TASK_TIMER_INVALID,
TASK_TIMER_INACTIVE,
TASK_TIMER_TICKS,
TASK_TIMER_REALTIME,
TASK_TIMER_MONOTONIC
} TaskTimerState;
typedef struct {
TaskTimerState state;
uint64_t expire_ticks;
struct timespec expire_timespec;
} TaskTimerInfo;
void GetTaskTimerInfo( rtems_id id, TaskTimerInfo *info );
void GetTaskTimerInfoByThread(
struct _Thread_Control *thread,
TaskTimerInfo *info
);
void ClockTick( void );
/**
* @brief Simulates a clock tick with the final expire time point of
* UINT64_MAX for all clocks.
*
* This function does not update the clock ticks counter.
*/
void FinalClockTick( void );
/**
* @brief Simulates a single clock tick using the software timecounter.
*
* In contrast to ClockTick(), this function updates also CLOCK_MONOTONIC and
* CLOCK_REALTIME to the next software timecounter clock tick time point.
*
* This function is designed for test suites not having a clock driver.
*/
void TimecounterTick( void );
typedef uint32_t ( *GetTimecountHandler )( void );
/**
* @brief Sets the get timecount handler.
*
* Using this function will replace the timecounter of the clock driver.
*
* @return Returns the previous get timecount handler.
*/
GetTimecountHandler SetGetTimecountHandler( GetTimecountHandler handler );
/**
* @brief This constant represents the fake frequency of the software
* timecounter.
*/
#define SOFTWARE_TIMECOUNTER_FREQUENCY 1000000
/**
* @brief Gets the software timecount counter value.
*
* @return Returns the current software timecounter counter value.
*/
uint32_t GetTimecountCounter( void );
/**
* @brief Sets and gets the software timecount counter value.
*
* @param counter is the new software timecounter counter value.
*
* @return Returns the previous software timecounter counter value.
*/
uint32_t SetTimecountCounter( uint32_t counter );
/**
* @brief Return the task id of the timer server task
*
* This function is an attempt to avoid using RTEMS internal global
* _Timer_server throughout the validation test code.
*
* @return Returns the task id of the timer server task, if
* rtems_timer_initiate_server() has been invoked before,
* otherwise - if the timer server task does not exist -
* RTEMS_INVALID_ID is returned.
*/
rtems_id GetTimerServerTaskId( void );
/**
* @brief Undo the effects of rtems_timer_initiate_server()
*
* If rtems_timer_initiate_server() was never called before,
* nothing is done.
*
* If rtems_timer_initiate_server() was called before, the
* created thread and other resources are freed so that
* rtems_timer_initiate_server() can be called again.
* There should be no pending timers which are not yet executed
* by the server task. Naturally, there should be no
* timer server timers scheduled for execution.
*
* @return Returns true, if rtems_timer_initiate_server() has been
* invoked before and the timer server task has indeed been deleted,
* otherwise false.
*/
bool DeleteTimerServer( void );
typedef struct {
struct {
const void *begin;
void *free_begin;
const void *end;
} areas[ 2 ];
size_t count;
} MemoryContext;
void MemorySave( MemoryContext *ctx );
void MemoryRestore( const MemoryContext *ctx );
/**
* @brief Fails a dynamic memory allocation when the counter reaches zero.
*
* This function initializes an internal counter which is decremented before
* each dynamic memory allocation though the rtems_malloc() directive. When
* the counter decrements from one to zero, the allocation fails and NULL will
* be returned.
*
* @param counter is the initial counter value.
*/
void MemoryAllocationFailWhen( uint32_t counter );
typedef struct {
Chain_Node node;
void ( *handler )( void * );
@@ -114,14 +388,208 @@ void CallWithinISRSubmit( CallWithinISRRequest *request );
void CallWithinISRWait( const CallWithinISRRequest *request );
typedef struct {
Thread_queue_Operations tq_ops;
const Thread_queue_Operations *wrapped_ops;
Thread_queue_Control thread_queue;
CallWithinISRRequest isr_request;
} WrapThreadQueueContext;
void WrapThreadQueueInitialize(
WrapThreadQueueContext *ctx,
void ( *handler )( void * ),
void *arg
);
void WrapThreadQueueExtract(
WrapThreadQueueContext *ctx,
struct _Thread_Control *thread
);
void WrapThreadQueueExtractDirect(
WrapThreadQueueContext *ctx,
Thread_Control *thread
);
void WrapThreadQueueDestroy( WrapThreadQueueContext *ctx );
struct Per_CPU_Control;
void SetPreemptionIntervention(
struct Per_CPU_Control *cpu,
void ( *handler )( void * ),
void *arg
);
rtems_vector_number GetValidInterruptVectorNumber(
const rtems_interrupt_attributes *required
);
rtems_vector_number GetTestableInterruptVector( void );
rtems_vector_number GetTestableInterruptVector(
const rtems_interrupt_attributes *required
);
bool HasInterruptVectorEntriesInstalled( rtems_vector_number vector );
/**
* @brief Get the clock and context of a timer from RTEMS internal data.
*
* With exception of TIMER_DORMANT, the return values are bits or-ed together.
*
* @param id The timer ID.
*
* @retval TIMER_DORMANT Either the id argument is invalid or the timer has
* never been used before.
* @return The TIMER_CLASS_BIT_ON_TASK is set, if the timer server routine
* was or will be executed in task context, otherwise it was or will be
* executed in interrupt context.
*
* The TIMER_CLASS_BIT_TIME_OF_DAY is set, if the clock used is or was the
* ${/glossary/clock-realtime:/term}, otherwise the
* ${/glossary/clock-tick:/term} based clock is or was used.
*/
Timer_Classes GetTimerClass( rtems_id id );
/**
* @brief This structure provides data used by RTEMS to schedule a timer
* service routine.
*/
typedef struct {
/**
* @brief This member contains a reference to the timer service routine.
*/
rtems_timer_service_routine_entry routine;
/**
* @brief This member contains a reference to the user data to be provided
* to the timer service routine.
*/
void *user_data;
/**
* @brief This member contains the timer interval in ticks or seconds.
*/
Watchdog_Interval interval;
} Timer_Scheduling_Data;
/**
* @brief Get data related to scheduling a timer service routine
* from RTEMS internal structures.
*
* @param id The timer ID.
* @param[out] data If the reference is not NULL, the data retrieved from
* internal RTEMS structures is stored here.
*/
void GetTimerSchedulingData(
rtems_id id,
Timer_Scheduling_Data *data
);
/**
* @brief The various states of a timer.
*/
typedef enum {
TIMER_INVALID,
TIMER_INACTIVE,
TIMER_SCHEDULED,
TIMER_PENDING
} Timer_States;
/**
* @brief Get the state of a timer from RTEMS internal data.
*
* @param id The timer ID.
*
* @retval TIMER_INVALID The id argument is invalid.
* @retval TIMER_INACTIVE The timer is not scheduled (i.e. it is
* new, run off, or canceled).
* @retval TIMER_SCHEDULED The timer is scheduled.
* @retval TIMER_PENDING The timer is pending.
*/
Timer_States GetTimerState( rtems_id id );
/**
* @brief Mark the realtime clock as never set.
*
* This function manipulates RTEMS internal data structures to undo the
* effect of rtems_clock_set(). If the clock is not set, the function has no
* effect.
*/
void UnsetClock( void );
void FatalInitialExtension(
rtems_fatal_source source,
bool always_set_to_false,
rtems_fatal_code code
);
typedef void ( *FatalHandler )(
rtems_fatal_source source,
rtems_fatal_code code,
void *arg
);
void SetFatalHandler( FatalHandler fatal, void *arg );
void SetTaskSwitchExtension( rtems_task_switch_extension task_switch );
typedef struct {
uint32_t fatal;
uint32_t thread_begin;
uint32_t thread_create;
uint32_t thread_delete;
uint32_t thread_exitted;
uint32_t thread_restart;
uint32_t thread_start;
uint32_t thread_switch;
uint32_t thread_terminate;
} ExtensionCalls;
void ClearExtensionCalls( ExtensionCalls *calls );
void CopyExtensionCalls( const ExtensionCalls *from, ExtensionCalls *to );
void SetIORelaxHandler( void ( *handler )( void * ), void *arg );
void StartDelayThreadDispatch( uint32_t cpu_index );
void StopDelayThreadDispatch( uint32_t cpu_index );
bool AreInterruptsEnabled( void );
bool IsWhiteSpaceOnly( const char *s );
bool IsEqualIgnoreWhiteSpace( const char *a, const char *b );
#if defined(RTEMS_SMP)
bool TicketLockIsAvailable( const SMP_ticket_lock_Control *lock );
void TicketLockWaitForOwned( const SMP_ticket_lock_Control *lock );
void TicketLockWaitForOthers(
const SMP_ticket_lock_Control *lock,
unsigned int others
);
static inline bool ISRLockIsAvailable( const ISR_lock_Control *lock )
{
return TicketLockIsAvailable( &lock->Lock.Ticket_lock );
}
static inline void ISRLockWaitForOwned( const ISR_lock_Control *lock )
{
TicketLockWaitForOwned( &lock->Lock.Ticket_lock );
}
static inline void ISRLockWaitForOthers(
const ISR_lock_Control *lock,
unsigned int others
)
{
TicketLockWaitForOthers( &lock->Lock.Ticket_lock, others );
}
#endif
void *IdleBody( uintptr_t ignored );
/** @} */
#ifdef __cplusplus

View File

@@ -0,0 +1,854 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the implementation of the thread queue test
* support.
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-thread-queue.h"
#include "tx-support.h"
#include "ts-config.h"
#include <rtems/score/threadimpl.h>
#include <rtems/rtems/semimpl.h>
void TQSend(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
)
{
#if defined( RTEMS_SMP )
ctx->event_received[ worker ] = false;
#endif
SendEvents( ctx->worker_id[ worker ], events );
}
void TQWaitForEventsReceived( const TQContext *ctx, TQWorkerKind worker )
{
#if defined( RTEMS_SMP )
while ( !ctx->event_received[ worker ] ) {
/* Wait */
}
#endif
}
void TQWaitForExecutionStop( const TQContext *ctx, TQWorkerKind worker )
{
#if defined( RTEMS_SMP )
WaitForExecutionStop( ctx->worker_id[ worker ] );
#endif
}
void TQSendAndWaitForExecutionStop(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
)
{
TQSend( ctx, worker, events );
#if defined( RTEMS_SMP )
TQWaitForEventsReceived( ctx, worker );
WaitForExecutionStop( ctx->worker_id[ worker ] );
#endif
}
void TQWaitForIntendToBlock( const TQContext *ctx, TQWorkerKind worker )
{
const rtems_tcb *thread;
Thread_Wait_flags intend_to_block;
thread = ctx->worker_tcb[ worker ];
intend_to_block = THREAD_WAIT_CLASS_OBJECT |
THREAD_WAIT_STATE_INTEND_TO_BLOCK;
while ( _Thread_Wait_flags_get_acquire( thread ) != intend_to_block ) {
/* Wait */
}
}
void TQSendAndWaitForIntendToBlock(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
)
{
TQSend( ctx, worker, events );
#if defined( RTEMS_SMP )
TQWaitForEventsReceived( ctx, worker );
TQWaitForIntendToBlock( ctx, worker );
#endif
}
void TQSendAndWaitForExecutionStopOrIntendToBlock(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
)
{
#if defined( RTEMS_SMP )
const rtems_tcb *thread;
Thread_Wait_flags intend_to_block;
#endif
TQSend( ctx, worker, events );
#if defined( RTEMS_SMP )
TQWaitForEventsReceived( ctx, worker );
thread = ctx->worker_tcb[ worker ];
intend_to_block = THREAD_WAIT_CLASS_OBJECT |
THREAD_WAIT_STATE_INTEND_TO_BLOCK;
while (
_Thread_Is_executing_on_a_processor( thread ) &&
_Thread_Wait_flags_get_acquire( thread ) != intend_to_block
) {
/* Wait */
}
#endif
}
void TQSendAndSynchronizeRunner(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
)
{
T_quiet_eq_u32( QueryPendingEvents() & TQ_EVENT_RUNNER_SYNC, 0 );
TQSend( ctx, worker, events | TQ_EVENT_RUNNER_SYNC );
TQSynchronizeRunner();
}
void TQClearDone( TQContext *ctx, TQWorkerKind worker )
{
ctx->done[ worker ] = false;
}
void TQWaitForDone( const TQContext *ctx, TQWorkerKind worker )
{
while ( !ctx->done[ worker ] ) {
/* Wait */
}
}
void TQSynchronizeRunner( void )
{
ReceiveAllEvents( TQ_EVENT_RUNNER_SYNC );
}
void TQSynchronizeRunner2( void )
{
ReceiveAllEvents( TQ_EVENT_RUNNER_SYNC | TQ_EVENT_RUNNER_SYNC_2 );
}
void TQResetCounter( TQContext *ctx )
{
ctx->counter = 0;
memset( &ctx->worker_counter, 0, sizeof( ctx->worker_counter ) );
}
uint32_t TQGetCounter( const TQContext *ctx )
{
return ctx->counter;
}
uint32_t TQGetWorkerCounter( const TQContext *ctx, TQWorkerKind worker )
{
return ctx->worker_counter[ worker ];
}
void TQMutexObtain( const TQContext *ctx, TQMutex mutex )
{
rtems_status_code sc;
sc = rtems_semaphore_obtain(
ctx->mutex_id[ mutex ],
RTEMS_WAIT,
RTEMS_NO_TIMEOUT
);
T_rsc_success( sc );
}
void TQMutexRelease( const TQContext *ctx, TQMutex mutex )
{
rtems_status_code sc;
sc = rtems_semaphore_release( ctx->mutex_id[ mutex ] );
T_rsc_success( sc );
}
void TQSetPriority(
const TQContext *ctx,
TQWorkerKind worker,
Priority priority
)
{
SetPriority( ctx->worker_id[ worker ], priority );
}
Priority TQGetPriority( const TQContext *ctx, TQWorkerKind worker )
{
return GetPriority( ctx->worker_id[ worker ] );
}
void TQSetScheduler(
const TQContext *ctx,
TQWorkerKind worker,
rtems_id scheduler_id,
Priority priority
)
{
#if defined( RTEMS_SMP )
rtems_status_code sc;
sc = rtems_task_set_scheduler(
ctx->worker_id[ worker ],
scheduler_id,
priority
);
T_rsc_success( sc );
#else
(void) scheduler_id;
SetPriority( ctx->worker_id[ worker ], priority );
#endif
}
static void Count( TQContext *ctx, TQWorkerKind worker )
{
unsigned int counter;
counter = _Atomic_Fetch_add_uint( &ctx->counter, 1, ATOMIC_ORDER_RELAXED );
ctx->worker_counter[ worker ] = counter + 1;
}
static void Enqueue( TQContext *ctx, TQWorkerKind worker, TQWait wait )
{
ctx->status[ worker ] = TQEnqueue( ctx, wait );
Count( ctx, worker );
}
static void ThreadQueueDeadlock(
rtems_fatal_source source,
rtems_fatal_code code,
void *arg
)
{
TQContext *ctx;
ctx = arg;
T_eq_int( source, INTERNAL_ERROR_CORE );
T_eq_int( code, INTERNAL_ERROR_THREAD_QUEUE_DEADLOCK );
SetFatalHandler( NULL, NULL );
longjmp( ctx->before_enqueue, 1 );
}
static void Worker( rtems_task_argument arg, TQWorkerKind worker )
{
TQContext *ctx;
ctx = (TQContext *) arg;
while ( true ) {
rtems_event_set events;
events = ReceiveAnyEvents();
ctx->event_received[ worker ] = true;
if ( ( events & TQ_EVENT_HELPER_A_SYNC ) != 0 ) {
SendEvents( ctx->worker_id[ TQ_HELPER_A ], TQ_EVENT_RUNNER_SYNC );
}
if ( ( events & TQ_EVENT_HELPER_B_SYNC ) != 0 ) {
SendEvents( ctx->worker_id[ TQ_HELPER_B ], TQ_EVENT_RUNNER_SYNC );
}
if ( ( events & TQ_EVENT_SCHEDULER_RECORD_START ) != 0 ) {
TQSchedulerRecordStart( ctx );
}
if ( ( events & TQ_EVENT_ENQUEUE_PREPARE ) != 0 ) {
TQEnqueuePrepare( ctx );
}
if ( ( events & TQ_EVENT_ENQUEUE ) != 0 ) {
Enqueue( ctx, worker, ctx->wait );
}
if ( ( events & TQ_EVENT_ENQUEUE_TIMED ) != 0 ) {
Enqueue( ctx, worker, TQ_WAIT_TIMED );
}
if ( ( events & TQ_EVENT_ENQUEUE_FATAL ) != 0 ) {
SetFatalHandler( ThreadQueueDeadlock, ctx );
if ( setjmp( ctx->before_enqueue ) == 0 ) {
ctx->status[ worker ] = STATUS_MINUS_ONE;
Enqueue( ctx, worker, ctx->wait );
} else {
ctx->status[ worker ] = STATUS_DEADLOCK;
}
}
if ( ( events & TQ_EVENT_TIMEOUT ) != 0 ) {
Per_CPU_Control *cpu_self;
cpu_self = _Thread_Dispatch_disable();
_Thread_Timeout( &ctx->worker_tcb[ worker ]->Timer.Watchdog );
_Thread_Dispatch_direct( cpu_self );
}
if ( ( events & TQ_EVENT_FLUSH ) != 0 ) {
TQFlush( ctx );
}
if ( ( events & TQ_EVENT_ENQUEUE_DONE ) != 0 ) {
TQEnqueueDone( ctx );
}
if ( ( events & TQ_EVENT_SURRENDER ) != 0 ) {
Status_Control status;
status = TQSurrender( ctx );
T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
}
if ( ( events & TQ_EVENT_MUTEX_A_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_A );
}
if ( ( events & TQ_EVENT_MUTEX_A_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_A );
}
if ( ( events & TQ_EVENT_MUTEX_B_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_B );
}
if ( ( events & TQ_EVENT_MUTEX_B_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_B );
}
if ( ( events & TQ_EVENT_MUTEX_C_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_C );
}
if ( ( events & TQ_EVENT_MUTEX_C_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_C );
}
if ( ( events & TQ_EVENT_MUTEX_D_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_D );
}
if ( ( events & TQ_EVENT_MUTEX_D_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_D );
}
if ( ( events & TQ_EVENT_MUTEX_NO_PROTOCOL_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_NO_PROTOCOL );
}
if ( ( events & TQ_EVENT_MUTEX_NO_PROTOCOL_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_NO_PROTOCOL );
}
if ( ( events & TQ_EVENT_MUTEX_FIFO_OBTAIN ) != 0 ) {
TQMutexObtain( ctx, TQ_MUTEX_FIFO );
}
if ( ( events & TQ_EVENT_MUTEX_FIFO_RELEASE ) != 0 ) {
TQMutexRelease( ctx, TQ_MUTEX_FIFO );
}
if ( ( events & TQ_EVENT_PIN ) != 0 ) {
_Thread_Pin( _Thread_Get_executing() );
}
if ( ( events & TQ_EVENT_UNPIN ) != 0 ) {
Per_CPU_Control *cpu_self;
cpu_self = _Thread_Dispatch_disable();
_Thread_Unpin( _Thread_Get_executing(), cpu_self );
_Thread_Dispatch_direct( cpu_self );
}
if ( ( events & TQ_EVENT_SCHEDULER_RECORD_STOP ) != 0 ) {
TQSchedulerRecordStop( ctx );
}
if ( ( events & TQ_EVENT_RUNNER_SYNC ) != 0 ) {
SendEvents( ctx->runner_id, TQ_EVENT_RUNNER_SYNC );
}
if ( ( events & TQ_EVENT_COUNT ) != 0 ) {
Count( ctx, worker );
}
if ( ( events & TQ_EVENT_BUSY_WAIT ) != 0 ) {
while ( ctx->busy_wait[ worker ] ) {
/* Wait */
}
}
if ( ( events & TQ_EVENT_RUNNER_SYNC_2 ) != 0 ) {
SendEvents( ctx->runner_id, TQ_EVENT_RUNNER_SYNC_2 );
}
ctx->done[ worker ] = true;
}
}
static void BlockerA( rtems_task_argument arg )
{
Worker( arg, TQ_BLOCKER_A );
}
static void BlockerB( rtems_task_argument arg )
{
Worker( arg, TQ_BLOCKER_B );
}
static void BlockerC( rtems_task_argument arg )
{
Worker( arg, TQ_BLOCKER_C );
}
static void BlockerD( rtems_task_argument arg )
{
Worker( arg, TQ_BLOCKER_D );
}
static void BlockerE( rtems_task_argument arg )
{
Worker( arg, TQ_BLOCKER_E );
}
static void WorkerF( rtems_task_argument arg )
{
Worker( arg, TQ_WORKER_F );
}
static void HelperA( rtems_task_argument arg )
{
Worker( arg, TQ_HELPER_A );
}
static void HelperB( rtems_task_argument arg )
{
Worker( arg, TQ_HELPER_B );
}
static void HelperC( rtems_task_argument arg )
{
Worker( arg, TQ_HELPER_C );
}
void TQInitialize( TQContext *ctx )
{
rtems_status_code sc;
size_t i;
ctx->runner_id = rtems_task_self();
ctx->runner_tcb = GetThread( RTEMS_SELF );
/*
* Use a lower priority than all started worker tasks to make sure they wait
* for events.
*/
SetSelfPriority( PRIO_VERY_LOW );
for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->mutex_id ); ++i ) {
rtems_attribute attributes;
attributes = RTEMS_BINARY_SEMAPHORE;
if ( i == TQ_MUTEX_NO_PROTOCOL ) {
attributes |= RTEMS_PRIORITY;
} else if ( i == TQ_MUTEX_FIFO ) {
attributes |= RTEMS_FIFO;
} else {
attributes |= RTEMS_INHERIT_PRIORITY | RTEMS_PRIORITY;
}
sc = rtems_semaphore_create(
rtems_build_name( 'M', 'T', 'X', 'A' + i ),
1,
attributes,
0,
&ctx->mutex_id[ i ]
);
T_rsc_success( sc );
}
ctx->worker_id[ TQ_BLOCKER_A ] = CreateTask( "BLKA", PRIO_HIGH );
StartTask( ctx->worker_id[ TQ_BLOCKER_A ], BlockerA, ctx );
ctx->worker_id[ TQ_BLOCKER_B ] = CreateTask( "BLKB", PRIO_VERY_HIGH );
StartTask( ctx->worker_id[ TQ_BLOCKER_B ], BlockerB, ctx );
ctx->worker_id[ TQ_BLOCKER_C ] = CreateTask( "BLKC", PRIO_ULTRA_HIGH );
StartTask( ctx->worker_id[ TQ_BLOCKER_C ], BlockerC, ctx );
ctx->worker_id[ TQ_BLOCKER_D ] = CreateTask( "BLKD", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_BLOCKER_D ], BlockerD, ctx );
ctx->worker_id[ TQ_BLOCKER_E ] = CreateTask( "BLKE", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_BLOCKER_E ], BlockerE, ctx );
ctx->worker_id[ TQ_WORKER_F ] = CreateTask( "WRKF", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_WORKER_F ], WorkerF, ctx );
ctx->worker_id[ TQ_HELPER_A ] = CreateTask( "HLPA", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_HELPER_A ], HelperA, ctx );
ctx->worker_id[ TQ_HELPER_B ] = CreateTask( "HLPB", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_HELPER_B ], HelperB, ctx );
ctx->worker_id[ TQ_HELPER_C ] = CreateTask( "HLPC", PRIO_LOW );
StartTask( ctx->worker_id[ TQ_HELPER_C ], HelperC, ctx );
for (i = 0; i < RTEMS_ARRAY_SIZE( ctx->worker_tcb ); ++i) {
ctx->worker_tcb[ i ] = GetThread( ctx->worker_id[ i ] );
}
SetSelfPriority( PRIO_NORMAL );
}
void TQDestroy( TQContext *ctx )
{
size_t i;
for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->worker_id ); ++i ) {
DeleteTask( ctx->worker_id[ i ] );
}
for ( i = 0; i < RTEMS_ARRAY_SIZE( ctx->mutex_id ); ++i ) {
if ( ctx->mutex_id[ i ] != 0 ) {
rtems_status_code sc;
sc = rtems_semaphore_delete( ctx->mutex_id[ i ] );
T_rsc_success( sc );
}
}
RestoreRunnerPriority();
}
void TQReset( TQContext *ctx )
{
rtems_id scheduler_id;
scheduler_id = SCHEDULER_A_ID;
SetScheduler( ctx->runner_id, scheduler_id, PRIO_NORMAL );
TQSetScheduler( ctx, TQ_BLOCKER_A, scheduler_id, PRIO_HIGH );
TQSetScheduler( ctx, TQ_BLOCKER_B, scheduler_id, PRIO_VERY_HIGH );
TQSetScheduler( ctx, TQ_BLOCKER_C, scheduler_id, PRIO_ULTRA_HIGH );
TQSetScheduler( ctx, TQ_BLOCKER_D, scheduler_id, PRIO_LOW );
TQSetScheduler( ctx, TQ_BLOCKER_E, scheduler_id, PRIO_LOW );
TQSetScheduler( ctx, TQ_HELPER_A, scheduler_id, PRIO_LOW );
TQSetScheduler( ctx, TQ_HELPER_B, scheduler_id, PRIO_LOW );
TQSetScheduler( ctx, TQ_HELPER_C, scheduler_id, PRIO_LOW );
}
void TQSortMutexesByID( TQContext *ctx )
{
size_t i;
size_t n;
n = 3;
/* Bubble sort */
for ( i = 1; i < n ; ++i ) {
size_t j;
for ( j = 0; j < n - i; ++j ) {
if ( ctx->mutex_id[ j ] > ctx->mutex_id[ j + 1 ] ) {
rtems_id tmp;
tmp = ctx->mutex_id[ j ];
ctx->mutex_id[ j ] = ctx->mutex_id[ j + 1 ];
ctx->mutex_id[ j + 1 ] = tmp;
}
}
}
}
void TQGetProperties( TQContext *ctx, TQWorkerKind enqueued_worker )
{
( *ctx->get_properties )( ctx, enqueued_worker );
}
Status_Control TQConvertStatus( TQContext *ctx, Status_Control status )
{
return ( *ctx->convert_status )( status );
}
void TQEnqueuePrepare( TQContext *ctx )
{
( *ctx->enqueue_prepare )( ctx );
}
Status_Control TQEnqueue( TQContext *ctx, TQWait wait )
{
return ( *ctx->enqueue )( ctx, wait );
}
Status_Control TQEnqueueFatal( TQContext *ctx )
{
Status_Control status;
SetFatalHandler( ThreadQueueDeadlock, ctx );
status = STATUS_MINUS_ONE;
if ( setjmp( ctx->before_enqueue ) == 0 ) {
status = TQEnqueue( ctx, ctx->wait );
} else {
status = STATUS_DEADLOCK;
}
return status;
}
void TQEnqueueDone( TQContext *ctx )
{
( *ctx->enqueue_done )( ctx );
}
Status_Control TQSurrender( TQContext *ctx )
{
return ( *ctx->surrender )( ctx );
}
void TQFlush( TQContext *ctx )
{
( *ctx->flush )( ctx );
}
rtems_tcb *TQGetOwner( TQContext *ctx )
{
rtems_tcb *( *get_owner )( TQContext * );
get_owner = ctx->get_owner;
if ( get_owner == NULL ) {
return NULL;
}
return ( *get_owner )( ctx );
}
void TQSchedulerRecordStart( TQContext *ctx )
{
T_scheduler_log *log;
log = T_scheduler_record_40( &ctx->scheduler_log );
T_null( log );
}
void TQSchedulerRecordStop( TQContext *ctx )
{
T_scheduler_log *log;
log = T_scheduler_record( NULL );
T_eq_ptr( &log->header, &ctx->scheduler_log.header );
}
const T_scheduler_event *TQGetNextAny( TQContext *ctx, size_t *index )
{
return T_scheduler_next_any(
&ctx->scheduler_log.header,
index
);
}
const T_scheduler_event *TQGetNextBlock( TQContext *ctx, size_t *index )
{
return T_scheduler_next(
&ctx->scheduler_log.header,
T_SCHEDULER_BLOCK,
index
);
}
const T_scheduler_event *TQGetNextUnblock( TQContext *ctx, size_t *index )
{
return T_scheduler_next(
&ctx->scheduler_log.header,
T_SCHEDULER_UNBLOCK,
index
);
}
const T_scheduler_event *TQGetNextUpdatePriority(
TQContext *ctx,
size_t *index
)
{
return T_scheduler_next(
&ctx->scheduler_log.header,
T_SCHEDULER_UPDATE_PRIORITY,
index
);
}
const T_scheduler_event *TQGetNextAskForHelp(
TQContext *ctx,
size_t *index
)
{
return T_scheduler_next(
&ctx->scheduler_log.header,
T_SCHEDULER_ASK_FOR_HELP,
index
);
}
void TQDoNothing( TQContext *ctx )
{
(void) ctx;
}
Status_Control TQDoNothingSuccessfully( TQContext *ctx )
{
(void) ctx;
return STATUS_SUCCESSFUL;
}
Status_Control TQConvertStatusClassic( Status_Control status )
{
return STATUS_BUILD( STATUS_GET_CLASSIC( status ), 0 );
}
Status_Control TQConvertStatusPOSIX( Status_Control status )
{
return STATUS_BUILD( 0, STATUS_GET_POSIX( status ) );
}
void TQEnqueuePrepareDefault( TQContext *ctx )
{
Status_Control status;
status = TQEnqueue( ctx, TQ_NO_WAIT );
T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
}
void TQEnqueueDoneDefault( TQContext *ctx )
{
Status_Control status;
status = TQSurrender( ctx );
T_eq_int( status, TQConvertStatus( ctx, STATUS_SUCCESSFUL ) );
}
Status_Control TQEnqueueClassicSem( TQContext *ctx, TQWait wait )
{
rtems_status_code sc;
rtems_option option;
rtems_option timeout;
switch ( wait ) {
case TQ_WAIT_FOREVER:
option = RTEMS_WAIT;
timeout = RTEMS_NO_TIMEOUT;
break;
case TQ_WAIT_TIMED:
option = RTEMS_WAIT;
timeout = UINT32_MAX;
break;
default:
option = RTEMS_NO_WAIT;
timeout = 0;
break;
}
sc = rtems_semaphore_obtain( ctx->thread_queue_id, option, timeout );
return STATUS_BUILD( sc, 0 );
}
Status_Control TQSurrenderClassicSem( TQContext *ctx )
{
rtems_status_code sc;
sc = rtems_semaphore_release( ctx->thread_queue_id );
return STATUS_BUILD( sc, 0 );
}
rtems_tcb *TQGetOwnerClassicSem( TQContext *ctx )
{
Semaphore_Control *semaphore;
Thread_queue_Context queue_context;
rtems_tcb *thread;
semaphore = _Semaphore_Get( ctx->thread_queue_id, &queue_context );
T_assert_not_null( semaphore );
thread = semaphore->Core_control.Wait_queue.Queue.owner;
_ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
return thread;
}
uint32_t TQSemGetCount( TQSemContext *ctx )
{
return ( *ctx->get_count )( ctx );
}
void TQSemSetCount( TQSemContext *ctx, uint32_t count )
{
( *ctx->set_count )( ctx, count );
}
uint32_t TQSemGetCountClassic( TQSemContext *ctx )
{
Semaphore_Control *semaphore;
Thread_queue_Context queue_context;
uint32_t count;
semaphore = _Semaphore_Get( ctx->base.thread_queue_id, &queue_context );
T_assert_not_null( semaphore );
count = semaphore->Core_control.Semaphore.count;
_ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
return count;
}
void TQSemSetCountClassic( TQSemContext *ctx, uint32_t count )
{
Semaphore_Control *semaphore;
Thread_queue_Context queue_context;
semaphore = _Semaphore_Get( ctx->base.thread_queue_id, &queue_context );
T_assert_not_null( semaphore );
semaphore->Core_control.Semaphore.count = count;
_ISR_lock_ISR_enable( &queue_context.Lock_context.Lock_context );
}

View File

@@ -0,0 +1,525 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This header file provides the functions to test the
* @ref RTEMSScoreThreadQueue.
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifndef _TX_THREAD_QUEUE_H
#define _TX_THREAD_QUEUE_H
#include "tx-support.h"
#include <rtems/test-scheduler.h>
#include <rtems/score/atomic.h>
#include <rtems/score/status.h>
#include <setjmp.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @addtogroup RTEMSTestSuites
*
* @{
*/
typedef enum {
TQ_NODE_ONLY,
TQ_NODE_VITAL,
TQ_NODE_DISPENSABLE
} TQNodeKind;
typedef enum {
TQ_WAIT_STATE_BLOCKED,
TQ_WAIT_STATE_INTEND_TO_BLOCK,
TQ_WAIT_STATE_READY_AGAIN
} TQWaitState;
typedef enum {
TQ_BLOCKER_A,
TQ_BLOCKER_B,
TQ_BLOCKER_C,
TQ_BLOCKER_D,
TQ_BLOCKER_E,
TQ_WORKER_F,
TQ_HELPER_A,
TQ_HELPER_B,
TQ_HELPER_C,
TQ_WORKER_COUNT
} TQWorkerKind;
typedef enum {
TQ_MUTEX_A,
TQ_MUTEX_B,
TQ_MUTEX_C,
TQ_MUTEX_D,
TQ_MUTEX_NO_PROTOCOL,
TQ_MUTEX_FIFO,
TQ_MUTEX_COUNT
} TQMutex;
typedef enum {
TQ_FIFO,
TQ_PRIORITY
} TQDiscipline;
typedef enum {
TQ_NO_WAIT,
TQ_WAIT_FOREVER,
TQ_WAIT_TIMED
} TQWait;
typedef enum {
TQ_DEADLOCK_STATUS,
TQ_DEADLOCK_FATAL
} TQDeadlock;
typedef enum {
TQ_EVENT_ENQUEUE_PREPARE = RTEMS_EVENT_0,
TQ_EVENT_ENQUEUE = RTEMS_EVENT_1,
TQ_EVENT_ENQUEUE_DONE = RTEMS_EVENT_2,
TQ_EVENT_SURRENDER = RTEMS_EVENT_3,
TQ_EVENT_RUNNER_SYNC = RTEMS_EVENT_4,
TQ_EVENT_RUNNER_SYNC_2 = RTEMS_EVENT_5,
TQ_EVENT_HELPER_A_SYNC = RTEMS_EVENT_6,
TQ_EVENT_HELPER_B_SYNC = RTEMS_EVENT_7,
TQ_EVENT_MUTEX_A_OBTAIN = RTEMS_EVENT_8,
TQ_EVENT_MUTEX_A_RELEASE = RTEMS_EVENT_9,
TQ_EVENT_MUTEX_B_OBTAIN = RTEMS_EVENT_10,
TQ_EVENT_MUTEX_B_RELEASE = RTEMS_EVENT_11,
TQ_EVENT_BUSY_WAIT = RTEMS_EVENT_12,
TQ_EVENT_FLUSH = RTEMS_EVENT_13,
TQ_EVENT_SCHEDULER_RECORD_START = RTEMS_EVENT_14,
TQ_EVENT_SCHEDULER_RECORD_STOP = RTEMS_EVENT_15,
TQ_EVENT_TIMEOUT = RTEMS_EVENT_16,
TQ_EVENT_MUTEX_NO_PROTOCOL_OBTAIN = RTEMS_EVENT_17,
TQ_EVENT_MUTEX_NO_PROTOCOL_RELEASE = RTEMS_EVENT_18,
TQ_EVENT_ENQUEUE_FATAL = RTEMS_EVENT_19,
TQ_EVENT_MUTEX_C_OBTAIN = RTEMS_EVENT_20,
TQ_EVENT_MUTEX_C_RELEASE = RTEMS_EVENT_21,
TQ_EVENT_MUTEX_FIFO_OBTAIN = RTEMS_EVENT_22,
TQ_EVENT_MUTEX_FIFO_RELEASE = RTEMS_EVENT_23,
TQ_EVENT_ENQUEUE_TIMED = RTEMS_EVENT_24,
TQ_EVENT_MUTEX_D_OBTAIN = RTEMS_EVENT_25,
TQ_EVENT_MUTEX_D_RELEASE = RTEMS_EVENT_26,
TQ_EVENT_PIN = RTEMS_EVENT_27,
TQ_EVENT_UNPIN = RTEMS_EVENT_28,
TQ_EVENT_COUNT = RTEMS_EVENT_29
} TQEvent;
typedef enum {
TQ_ENQUEUE_BLOCKS,
TQ_ENQUEUE_STICKY
} TQEnqueueVariant;
typedef struct TQContext {
/**
* @brief This member defines the thread queue discipline.
*/
TQDiscipline discipline;
/**
* @brief This member defines the enqueue wait behaviour.
*
* If TQ_NO_WAIT is used, then no thread queue enqueue shall be performed.
*/
TQWait wait;
/**
* @brief This member defines the enqueue variant.
*/
TQEnqueueVariant enqueue_variant;
/**
* @brief This member defines the deadlock enqueue behaviour.
*/
TQDeadlock deadlock;
/**
* @brief This member contains the runner task identifier.
*/
rtems_id runner_id;
/**
* @brief This member contains a reference to the runner task control block.
*/
rtems_tcb *runner_tcb;
/**
* @brief This member contains the worker task identifiers.
*/
rtems_id worker_id[ TQ_WORKER_COUNT ];
/**
* @brief This member contains references to the worker task control
* blocks.
*/
rtems_tcb *worker_tcb[ TQ_WORKER_COUNT ];
/**
* @brief When a worker received an event, the corresponding element shall be
* set to true.
*/
volatile bool event_received[ TQ_WORKER_COUNT ];
/**
* @brief If this member is true, then the worker shall busy wait on request.
*/
volatile bool busy_wait[ TQ_WORKER_COUNT ];
/**
* @brief When a worker is done processing its current event set, the
* corresponding element shall be set to true.
*/
volatile bool done[ TQ_WORKER_COUNT ];
/**
* @brief This member provides the counter used for the worker counters.
*/
Atomic_Uint counter;
/**
* @brief When a worker returned from TQEnqueue() the counter is incremented
* and stored in this member.
*/
uint32_t worker_counter[ TQ_WORKER_COUNT ];
/**
* @brief This member contains the last return status of a TQEnqueue() of the
* corresponding worker.
*/
Status_Control status[ TQ_WORKER_COUNT ];
union {
/**
* @brief This member contains the identifier of an object providing the
* thread queue under test.
*/
rtems_id thread_queue_id;
/**
* @brief This member contains the reference to object containing the
* thread queue under test.
*/
void *thread_queue_object;
};
/**
* @brief This member contains the identifier of priority inheritance
* mutexes.
*/
rtems_id mutex_id[ TQ_MUTEX_COUNT ];
/**
* @brief This member provides the scheduler log.
*/
T_scheduler_log_40 scheduler_log;
/**
* @brief This member provides the get properties handler.
*/
void ( *get_properties )( struct TQContext *, TQWorkerKind );
/**
* @brief This member provides the status convert handler.
*/
Status_Control ( *convert_status )( Status_Control );
/**
* @brief This this member specifies how many threads shall be enqueued.
*/
uint32_t how_many;
/**
* @brief This this member provides a context to jump back to before the
* enqueue.
*/
jmp_buf before_enqueue;
/**
* @brief This member provides the thread queue enqueue prepare handler.
*/
void ( *enqueue_prepare )( struct TQContext * );
/**
* @brief This member provides the thread queue enqueue handler.
*/
Status_Control ( *enqueue )( struct TQContext *, TQWait );
/**
* @brief This member provides the thread queue enqueue done handler.
*/
void ( *enqueue_done )( struct TQContext * );
/**
* @brief This member provides the thread queue surrender handler.
*/
Status_Control ( *surrender )( struct TQContext * );
/**
* @brief This member provides the thread queue flush handler.
*/
void ( *flush )( struct TQContext * );
/**
* @brief This member provides the get owner handler.
*/
rtems_tcb *( *get_owner )( struct TQContext * );
} TQContext;
void TQSend(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
);
void TQSendAndWaitForExecutionStop(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
);
void TQSendAndWaitForIntendToBlock(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
);
void TQSendAndWaitForExecutionStopOrIntendToBlock(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
);
void TQSendAndSynchronizeRunner(
TQContext *ctx,
TQWorkerKind worker,
rtems_event_set events
);
void TQWaitForEventsReceived( const TQContext *ctx, TQWorkerKind worker );
void TQWaitForIntendToBlock( const TQContext *ctx, TQWorkerKind worker );
void TQWaitForExecutionStop( const TQContext *ctx, TQWorkerKind worker );
void TQClearDone( TQContext *ctx, TQWorkerKind worker );
void TQWaitForDone( const TQContext *ctx, TQWorkerKind worker );
void TQSynchronizeRunner( void );
void TQSynchronizeRunner2( void );
void TQResetCounter( TQContext *ctx );
uint32_t TQGetCounter( const TQContext *ctx );
uint32_t TQGetWorkerCounter( const TQContext *ctx, TQWorkerKind worker );
void TQMutexObtain( const TQContext *ctx, TQMutex mutex );
void TQMutexRelease( const TQContext *ctx, TQMutex mutex );
void TQSetPriority(
const TQContext *ctx,
TQWorkerKind worker,
Priority priority
);
Priority TQGetPriority( const TQContext *ctx, TQWorkerKind worker );
void TQSetScheduler(
const TQContext *ctx,
TQWorkerKind worker,
rtems_id scheduler_id,
Priority priority
);
void TQInitialize( TQContext *ctx );
void TQDestroy( TQContext *ctx );
void TQReset( TQContext *ctx );
void TQSortMutexesByID( TQContext *ctx );
void TQGetProperties( TQContext *ctx, TQWorkerKind enqueued_worker );
Status_Control TQConvertStatus( TQContext *ctx, Status_Control status );
void TQEnqueuePrepare( TQContext *ctx );
Status_Control TQEnqueue( TQContext *ctx, TQWait wait );
Status_Control TQEnqueueFatal( TQContext *ctx );
void TQEnqueueDone( TQContext *ctx );
Status_Control TQSurrender( TQContext *ctx );
void TQFlush( TQContext *ctx );
rtems_tcb *TQGetOwner( TQContext *ctx );
void TQSchedulerRecordStart( TQContext *ctx );
void TQSchedulerRecordStop( TQContext *ctx );
const T_scheduler_event *TQGetNextAny( TQContext *ctx, size_t *index );
const T_scheduler_event *TQGetNextBlock( TQContext *ctx, size_t *index );
const T_scheduler_event *TQGetNextUnblock( TQContext *ctx, size_t *index );
const T_scheduler_event *TQGetNextUpdatePriority(
TQContext *ctx,
size_t *index
);
const T_scheduler_event *TQGetNextAskForHelp( TQContext *ctx, size_t *index );
void TQDoNothing( TQContext *ctx );
Status_Control TQDoNothingSuccessfully( TQContext *ctx );
Status_Control TQConvertStatusClassic( Status_Control status );
Status_Control TQConvertStatusPOSIX( Status_Control status );
void TQEnqueuePrepareDefault( TQContext *ctx );
void TQEnqueueDoneDefault( TQContext *ctx );
Status_Control TQEnqueueClassicSem( TQContext *ctx, TQWait wait );
Status_Control TQSurrenderClassicSem( TQContext *ctx );
rtems_tcb *TQGetOwnerClassicSem( TQContext *ctx );
typedef enum {
TQ_SEM_BINARY,
TQ_SEM_COUNTING
} TQSemVariant;
typedef struct TQSemContext {
/**
* @brief This member contains the base thread queue test context.
*/
TQContext base;
/**
* @brief This member defines the semaphore variant.
*/
TQSemVariant variant;
/**
* @brief This member provides the semaphore get count handler.
*/
uint32_t ( *get_count )( struct TQSemContext * );
/**
* @brief This member provides the semaphore set count handler.
*/
void ( *set_count )( struct TQSemContext *, uint32_t );
} TQSemContext;
Status_Control TQSemSurrender( TQSemContext *ctx );
uint32_t TQSemGetCount( TQSemContext *ctx );
void TQSemSetCount( TQSemContext *ctx, uint32_t count );
Status_Control TQSemSurrenderClassic( TQSemContext *ctx );
uint32_t TQSemGetCountClassic( TQSemContext *ctx );
void TQSemSetCountClassic( TQSemContext *ctx, uint32_t count );
typedef enum {
TQ_MTX_NO_PROTOCOL,
TQ_MTX_PRIORITY_INHERIT,
TQ_MTX_PRIORITY_CEILING,
TQ_MTX_MRSP
} TQMtxProtocol;
typedef enum {
TQ_MTX_RECURSIVE_ALLOWED,
TQ_MTX_RECURSIVE_DEADLOCK,
TQ_MTX_RECURSIVE_UNAVAILABLE
} TQMtxRecursive;
typedef enum {
TQ_MTX_NO_OWNER_CHECK,
TQ_MTX_CHECKS_OWNER
} TQMtxOwnerCheck;
typedef struct TQMtxContext {
/**
* @brief This member contains the base thread queue test context.
*/
TQContext base;
/**
* @brief This member defines the locking protocol.
*/
TQMtxProtocol protocol;
/**
* @brief This member defines the recursive seize behaviour.
*/
TQMtxRecursive recursive;
/**
* @brief This member defines the owner check behaviour.
*/
TQMtxOwnerCheck owner_check;
/**
* @brief This member defines the priority ceiling of the mutex.
*
* Use PRIO_INVALID to indicate that the mutex does not provide a priority
* ceiling.
*/
rtems_task_priority priority_ceiling;
} TQMtxContext;
/** @} */
#ifdef __cplusplus
}
#endif
#endif /* _TX_THREAD_QUEUE_H */

View File

@@ -0,0 +1,160 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the definition of SetGetTimecountHandler(),
* GetTimecountCounter(), and SetTimecountCounter().
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
#include <rtems/sysinit.h>
#include <rtems/timecounter.h>
#include <rtems/score/atomic.h>
#include <rtems/score/percpu.h>
#include <rtems/score/smpimpl.h>
#include <rtems/score/threaddispatch.h>
typedef struct {
struct timecounter base;
GetTimecountHandler handler;
Atomic_Ulong counter;
} TimecounterControl;
static TimecounterControl TimecounterInstance;
GetTimecountHandler SetGetTimecountHandler( GetTimecountHandler handler )
{
GetTimecountHandler previous;
previous = TimecounterInstance.handler;
TimecounterInstance.handler = handler;
return previous;
}
uint32_t GetTimecountCounter( void )
{
return (uint32_t) _Atomic_Load_ulong(
&TimecounterInstance.counter,
ATOMIC_ORDER_RELAXED
);
}
uint32_t SetTimecountCounter( uint32_t counter )
{
return (uint32_t) _Atomic_Exchange_ulong(
&TimecounterInstance.counter,
counter,
ATOMIC_ORDER_RELAXED
);
}
static uint32_t GetTimecountSoftware( void )
{
return (uint32_t) _Atomic_Fetch_add_ulong(
&TimecounterInstance.counter,
1,
ATOMIC_ORDER_RELAXED
);
}
static uint32_t GetTimecountWrapper( struct timecounter *tc )
{
TimecounterControl *self;
self = (TimecounterControl *) tc;
return ( *self->handler )();
}
static void InstallTimecounter( void )
{
TimecounterControl *self;
self = &TimecounterInstance;
self->handler = GetTimecountSoftware;
self->base.tc_get_timecount = GetTimecountWrapper;
self->base.tc_counter_mask = 0xffffffff;
self->base.tc_frequency = SOFTWARE_TIMECOUNTER_FREQUENCY;
self->base.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER + 1;
rtems_timecounter_install( &self->base );
}
RTEMS_SYSINIT_ITEM(
InstallTimecounter,
RTEMS_SYSINIT_DEVICE_DRIVERS,
RTEMS_SYSINIT_ORDER_LAST
);
static void DoTimecounterTick( void *arg )
{
(void) arg;
_Timecounter_Tick();
}
void TimecounterTick( void )
{
unsigned long counter_ticks_per_clock_tick;
Per_CPU_Control *cpu_self;
bool success;
counter_ticks_per_clock_tick =
SOFTWARE_TIMECOUNTER_FREQUENCY / rtems_clock_get_ticks_per_second();
cpu_self = _Thread_Dispatch_disable();
do {
unsigned long old_counter;
unsigned long new_counter;
old_counter = _Atomic_Load_ulong(
&TimecounterInstance.counter,
ATOMIC_ORDER_RELAXED
);
new_counter = old_counter + counter_ticks_per_clock_tick -
( old_counter % counter_ticks_per_clock_tick );
success = _Atomic_Compare_exchange_ulong(
&TimecounterInstance.counter,
&old_counter,
new_counter,
ATOMIC_ORDER_RELAXED,
ATOMIC_ORDER_RELAXED
);
} while ( !success );
DoTimecounterTick( NULL );
#if defined( RTEMS_SMP )
_SMP_Othercast_action( DoTimecounterTick, NULL );
#endif
_Thread_Dispatch_enable( cpu_self );
}

View File

@@ -0,0 +1,150 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the definition of DeleteTimerServer().
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
#include <rtems/test.h>
#include <rtems/rtems/timerimpl.h>
#include <rtems/score/todimpl.h>
rtems_id GetTimerServerTaskId( void )
{
if ( _Timer_server == NULL ) {
return RTEMS_INVALID_ID;
}
return _Timer_server->server_id;
}
bool DeleteTimerServer( void )
{
Timer_server_Control *server;
server = _Timer_server;
if ( server == NULL ) {
return false;
}
DeleteTask( server->server_id );
_ISR_lock_Destroy( &server->Lock );
T_true( _Chain_Is_empty( &server->Pending ) );
_Timer_server = NULL;
return true;
}
Timer_Classes GetTimerClass( rtems_id id )
{
/* This code is derived from rtems_timer_get_information() */
Timer_Classes result = TIMER_DORMANT;
Timer_Control *the_timer;
ISR_lock_Context lock_context;
Per_CPU_Control *cpu;
the_timer = _Timer_Get( id, &lock_context );
if ( the_timer != NULL ) {
cpu = _Timer_Acquire_critical( the_timer, &lock_context );
result = the_timer->the_class;
_Timer_Release( cpu, &lock_context );
}
return result;
}
void GetTimerSchedulingData(
rtems_id id,
Timer_Scheduling_Data *data
)
{
/* This code is derived from rtems_timer_get_information() */
Timer_Control *the_timer;
ISR_lock_Context lock_context;
Per_CPU_Control *cpu;
if ( data == NULL ) {
return;
}
the_timer = _Timer_Get( id, &lock_context );
if ( the_timer != NULL ) {
cpu = _Timer_Acquire_critical( the_timer, &lock_context );
data->routine = the_timer->routine;
data->user_data = the_timer->user_data;
data->interval = the_timer->initial;
_Timer_Release( cpu, &lock_context );
}
}
Timer_States GetTimerState( rtems_id id )
{
/* This code is derived from rtems_timer_cancel() and _timer_cancel() */
Timer_States result = TIMER_INVALID;
Timer_Control *the_timer;
ISR_lock_Context lock_context;
Per_CPU_Control *cpu;
Timer_Classes the_class;
Timer_server_Control *timer_server = _Timer_server;
ISR_lock_Context lock_context_server;
the_timer = _Timer_Get( id, &lock_context );
if ( the_timer != NULL ) {
result = TIMER_INACTIVE;
cpu = _Timer_Acquire_critical( the_timer, &lock_context );
the_class = the_timer->the_class;
if ( _Watchdog_Is_scheduled( &the_timer->Ticker ) ) {
result = TIMER_SCHEDULED;
} else if ( _Timer_Is_on_task_class( the_class ) ) {
_Assert( timer_server != NULL );
_Timer_server_Acquire_critical( timer_server, &lock_context_server );
if ( _Watchdog_Get_state( &the_timer->Ticker ) == WATCHDOG_PENDING ) {
result = TIMER_PENDING;
}
_Timer_server_Release_critical( timer_server, &lock_context_server );
}
_Timer_Release( cpu, &lock_context );
}
return result;
}
void UnsetClock( void )
{
_TOD.is_set = false;
}

View File

@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: BSD-2-Clause */
/**
* @file
*
* @ingroup RTEMSTestSuites
*
* @brief This source file contains the implementation of the thread queue
* wrapper.
*/
/*
* Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de)
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "tx-support.h"
#include <rtems/score/threadimpl.h>
#include <rtems/score/threadqimpl.h>
#include <string.h>
void WrapThreadQueueInitialize(
WrapThreadQueueContext *ctx,
void ( *handler )( void * ),
void *arg
)
{
memset( ctx, 0, sizeof( *ctx ) );
ctx->isr_request.handler = handler;
ctx->isr_request.arg = arg;
_Thread_queue_Initialize( &ctx->thread_queue, "Wrap" );
}
static void Prepare(
WrapThreadQueueContext *ctx,
Thread_Control *thread
)
{
if ( thread->Wait.queue != NULL ) {
ctx->wrapped_ops = thread->Wait.operations;
thread->Wait.operations = &ctx->tq_ops;
} else {
Thread_queue_Context queue_context;
ctx->wrapped_ops = NULL;
_Thread_queue_Context_initialize( &queue_context );
_Thread_queue_Acquire( &ctx->thread_queue, &queue_context );
_Thread_Wait_flags_set(
thread,
THREAD_WAIT_CLASS_OBJECT | THREAD_WAIT_STATE_INTEND_TO_BLOCK
);
_Thread_Wait_claim( thread, &ctx->thread_queue.Queue );
_Thread_Wait_claim_finalize( thread, &ctx->tq_ops );
_Thread_queue_Release( &ctx->thread_queue, &queue_context );
}
}
static void WrappedExtract(
WrapThreadQueueContext *ctx,
Thread_queue_Queue *queue,
Thread_Control *thread,
Thread_queue_Context *queue_context
)
{
if ( ctx->wrapped_ops ) {
thread->Wait.operations = ctx->wrapped_ops;
( *thread->Wait.operations->extract )( queue, thread, queue_context );
}
}
static void Extract(
Thread_queue_Queue *queue,
Thread_Control *thread,
Thread_queue_Context *queue_context
)
{
WrapThreadQueueContext *ctx;
ctx = (WrapThreadQueueContext *) thread->Wait.operations;
CallWithinISRSubmit( &ctx->isr_request );
WrappedExtract( ctx, queue, thread, queue_context );
}
void WrapThreadQueueExtract(
WrapThreadQueueContext *ctx,
Thread_Control *thread
)
{
ctx->tq_ops.extract = Extract;
Prepare( ctx, thread );
}
static void ExtractDirect(
Thread_queue_Queue *queue,
Thread_Control *thread,
Thread_queue_Context *queue_context
)
{
WrapThreadQueueContext *ctx;
ctx = (WrapThreadQueueContext *) thread->Wait.operations;
( *ctx->isr_request.handler )( ctx->isr_request.arg );
WrappedExtract( ctx, queue, thread, queue_context );
}
void WrapThreadQueueExtractDirect(
WrapThreadQueueContext *ctx,
Thread_Control *thread
)
{
ctx->tq_ops.extract = ExtractDirect;
Prepare( ctx, thread );
}
void WrapThreadQueueDestroy( WrapThreadQueueContext *ctx )
{
_Thread_queue_Destroy( &ctx->thread_queue );
}