mirror of
https://gitlab.rtems.org/rtems/rtos/rtems.git
synced 2026-09-28 00:38:38 +08:00
score: Use chain iterator for user extensions
Add a lock and use a chain iterator for safe iteration during concurrent user extension addition and removal. Ensure that dynamically added thread delete and fatal extensions are called in reverse order. Update #2555. Update #2692.
This commit is contained in:
@@ -48,6 +48,8 @@ struct Scheduler_Control;
|
|||||||
|
|
||||||
struct Scheduler_Node;
|
struct Scheduler_Node;
|
||||||
|
|
||||||
|
struct User_extensions_Iterator;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -898,6 +900,11 @@ struct _Thread_Control {
|
|||||||
*/
|
*/
|
||||||
struct _pthread_cleanup_context *last_cleanup_context;
|
struct _pthread_cleanup_context *last_cleanup_context;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LIFO list of user extensions iterators.
|
||||||
|
*/
|
||||||
|
struct User_extensions_Iterator *last_user_extensions_iterator;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Variable length array of user extension pointers.
|
* @brief Variable length array of user extension pointers.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#define _RTEMS_SCORE_USEREXTIMPL_H
|
#define _RTEMS_SCORE_USEREXTIMPL_H
|
||||||
|
|
||||||
#include <rtems/score/userext.h>
|
#include <rtems/score/userext.h>
|
||||||
|
#include <rtems/score/isrlock.h>
|
||||||
#include <rtems/score/chainimpl.h>
|
#include <rtems/score/chainimpl.h>
|
||||||
#include <rtems/score/percpu.h>
|
#include <rtems/score/percpu.h>
|
||||||
|
|
||||||
@@ -35,10 +36,42 @@ extern "C" {
|
|||||||
*/
|
*/
|
||||||
/**@{**/
|
/**@{**/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Chain iterator for dynamic user extensions.
|
||||||
|
*
|
||||||
|
* Since user extensions may delete or restart the executing thread, we must
|
||||||
|
* clean up registered iterators.
|
||||||
|
*
|
||||||
|
* @see _User_extensions_Iterate(), _User_extensions_Destroy_iterators() and
|
||||||
|
* Thread_Control::last_user_extensions_iterator.
|
||||||
|
*/
|
||||||
|
typedef struct User_extensions_Iterator {
|
||||||
|
Chain_Iterator Iterator;
|
||||||
|
struct User_extensions_Iterator *previous;
|
||||||
|
} User_extensions_Iterator;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/**
|
||||||
|
* @brief Active dynamically added user extensions.
|
||||||
|
*/
|
||||||
|
Chain_Control Active;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Chain iterator registration.
|
||||||
|
*/
|
||||||
|
Chain_Iterator_registry Iterators;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Lock to protect User_extensions_List::Active and
|
||||||
|
* User_extensions_List::Iterators.
|
||||||
|
*/
|
||||||
|
ISR_LOCK_MEMBER( Lock )
|
||||||
|
} User_extensions_List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of active extensions.
|
* @brief List of active extensions.
|
||||||
*/
|
*/
|
||||||
extern Chain_Control _User_extensions_List;
|
extern User_extensions_List _User_extensions_List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief List of active task switch extensions.
|
* @brief List of active task switch extensions.
|
||||||
@@ -153,11 +186,13 @@ void _User_extensions_Thread_terminate_visitor(
|
|||||||
* @brief Iterates through all user extensions and calls the visitor for each.
|
* @brief Iterates through all user extensions and calls the visitor for each.
|
||||||
*
|
*
|
||||||
* @param[in, out] arg The argument passed to the visitor.
|
* @param[in, out] arg The argument passed to the visitor.
|
||||||
* @param[in] visitor is the visitor for each extension.
|
* @param[in] visitor The visitor for each extension.
|
||||||
|
* @param[in] direction The iteration direction for dynamic extensions.
|
||||||
*/
|
*/
|
||||||
void _User_extensions_Iterate(
|
void _User_extensions_Iterate(
|
||||||
void *arg,
|
void *arg,
|
||||||
User_extensions_Visitor visitor
|
User_extensions_Visitor visitor,
|
||||||
|
Chain_Iterator_direction direction
|
||||||
);
|
);
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
@@ -171,7 +206,11 @@ static inline bool _User_extensions_Thread_create( Thread_Control *created )
|
|||||||
{
|
{
|
||||||
User_extensions_Thread_create_context ctx = { created, true };
|
User_extensions_Thread_create_context ctx = { created, true };
|
||||||
|
|
||||||
_User_extensions_Iterate( &ctx, _User_extensions_Thread_create_visitor );
|
_User_extensions_Iterate(
|
||||||
|
&ctx,
|
||||||
|
_User_extensions_Thread_create_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
|
);
|
||||||
|
|
||||||
return ctx.ok;
|
return ctx.ok;
|
||||||
}
|
}
|
||||||
@@ -180,7 +219,8 @@ static inline void _User_extensions_Thread_delete( Thread_Control *deleted )
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
deleted,
|
deleted,
|
||||||
_User_extensions_Thread_delete_visitor
|
_User_extensions_Thread_delete_visitor,
|
||||||
|
CHAIN_ITERATOR_BACKWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +228,8 @@ static inline void _User_extensions_Thread_start( Thread_Control *started )
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
started,
|
started,
|
||||||
_User_extensions_Thread_start_visitor
|
_User_extensions_Thread_start_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +237,8 @@ static inline void _User_extensions_Thread_restart( Thread_Control *restarted )
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
restarted,
|
restarted,
|
||||||
_User_extensions_Thread_restart_visitor
|
_User_extensions_Thread_restart_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +246,8 @@ static inline void _User_extensions_Thread_begin( Thread_Control *executing )
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
executing,
|
executing,
|
||||||
_User_extensions_Thread_begin_visitor
|
_User_extensions_Thread_begin_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,7 +282,8 @@ static inline void _User_extensions_Thread_exitted( Thread_Control *executing )
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
executing,
|
executing,
|
||||||
_User_extensions_Thread_exitted_visitor
|
_User_extensions_Thread_exitted_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -251,7 +295,11 @@ static inline void _User_extensions_Fatal(
|
|||||||
{
|
{
|
||||||
User_extensions_Fatal_context ctx = { source, is_internal, error };
|
User_extensions_Fatal_context ctx = { source, is_internal, error };
|
||||||
|
|
||||||
_User_extensions_Iterate( &ctx, _User_extensions_Fatal_visitor );
|
_User_extensions_Iterate(
|
||||||
|
&ctx,
|
||||||
|
_User_extensions_Fatal_visitor,
|
||||||
|
CHAIN_ITERATOR_BACKWARD
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void _User_extensions_Thread_terminate(
|
static inline void _User_extensions_Thread_terminate(
|
||||||
@@ -260,10 +308,46 @@ static inline void _User_extensions_Thread_terminate(
|
|||||||
{
|
{
|
||||||
_User_extensions_Iterate(
|
_User_extensions_Iterate(
|
||||||
executing,
|
executing,
|
||||||
_User_extensions_Thread_terminate_visitor
|
_User_extensions_Thread_terminate_visitor,
|
||||||
|
CHAIN_ITERATOR_FORWARD
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void _User_extensions_Acquire( ISR_lock_Context *lock_context )
|
||||||
|
{
|
||||||
|
_ISR_lock_ISR_disable_and_acquire(
|
||||||
|
&_User_extensions_List.Lock,
|
||||||
|
lock_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _User_extensions_Release( ISR_lock_Context *lock_context )
|
||||||
|
{
|
||||||
|
_ISR_lock_Release_and_ISR_enable(
|
||||||
|
&_User_extensions_List.Lock,
|
||||||
|
lock_context
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void _User_extensions_Destroy_iterators(
|
||||||
|
Thread_Control *the_thread
|
||||||
|
)
|
||||||
|
{
|
||||||
|
ISR_lock_Context lock_context;
|
||||||
|
User_extensions_Iterator *iter;
|
||||||
|
|
||||||
|
_User_extensions_Acquire( &lock_context );
|
||||||
|
|
||||||
|
iter = the_thread->last_user_extensions_iterator;
|
||||||
|
|
||||||
|
while ( iter != NULL ) {
|
||||||
|
_Chain_Iterator_destroy( &iter->Iterator );
|
||||||
|
iter = iter->previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
_User_extensions_Release( &lock_context );
|
||||||
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|||||||
@@ -97,6 +97,7 @@ static void _Thread_Free( Thread_Control *the_thread )
|
|||||||
_Objects_Get_information_id( the_thread->Object.id );
|
_Objects_Get_information_id( the_thread->Object.id );
|
||||||
|
|
||||||
_User_extensions_Thread_delete( the_thread );
|
_User_extensions_Thread_delete( the_thread );
|
||||||
|
_User_extensions_Destroy_iterators( the_thread );
|
||||||
_ISR_lock_Destroy( &the_thread->Keys.Lock );
|
_ISR_lock_Destroy( &the_thread->Keys.Lock );
|
||||||
_Scheduler_Node_destroy( _Scheduler_Get( the_thread ), the_thread );
|
_Scheduler_Node_destroy( _Scheduler_Get( the_thread ), the_thread );
|
||||||
_ISR_lock_Destroy( &the_thread->Timer.Lock );
|
_ISR_lock_Destroy( &the_thread->Timer.Lock );
|
||||||
@@ -255,6 +256,7 @@ void _Thread_Life_action_handler(
|
|||||||
|
|
||||||
executing->Life.state = THREAD_LIFE_NORMAL;
|
executing->Life.state = THREAD_LIFE_NORMAL;
|
||||||
|
|
||||||
|
_User_extensions_Destroy_iterators( executing );
|
||||||
_Thread_Load_environment( executing );
|
_Thread_Load_environment( executing );
|
||||||
_Thread_Restart_self( executing );
|
_Thread_Restart_self( executing );
|
||||||
RTEMS_UNREACHABLE();
|
RTEMS_UNREACHABLE();
|
||||||
|
|||||||
@@ -20,20 +20,20 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <rtems/score/userextimpl.h>
|
#include <rtems/score/userextimpl.h>
|
||||||
#include <rtems/score/objectimpl.h>
|
|
||||||
#include <rtems/score/percpu.h>
|
#include <rtems/score/percpu.h>
|
||||||
#include <rtems/score/sysstate.h>
|
|
||||||
|
|
||||||
void _User_extensions_Add_set(
|
void _User_extensions_Add_set(
|
||||||
User_extensions_Control *the_extension
|
User_extensions_Control *the_extension
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_Assert(
|
ISR_lock_Context lock_context;
|
||||||
_Objects_Allocator_is_owner()
|
|
||||||
|| _System_state_Is_before_multitasking( _System_state_Get() )
|
|
||||||
);
|
|
||||||
|
|
||||||
_Chain_Append_unprotected( &_User_extensions_List, &the_extension->Node );
|
_User_extensions_Acquire( &lock_context );
|
||||||
|
_Chain_Append_unprotected(
|
||||||
|
&_User_extensions_List.Active,
|
||||||
|
&the_extension->Node
|
||||||
|
);
|
||||||
|
_User_extensions_Release( &lock_context );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a switch handler is present, append it to the switch chain.
|
* If a switch handler is present, append it to the switch chain.
|
||||||
|
|||||||
@@ -7,10 +7,10 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012 embedded brains GmbH. All rights reserved.
|
* Copyright (c) 2012, 2016 embedded brains GmbH. All rights reserved.
|
||||||
*
|
*
|
||||||
* embedded brains GmbH
|
* embedded brains GmbH
|
||||||
* Obere Lagerstr. 30
|
* Dornierstr. 4
|
||||||
* 82178 Puchheim
|
* 82178 Puchheim
|
||||||
* Germany
|
* Germany
|
||||||
* <rtems@embedded-brains.de>
|
* <rtems@embedded-brains.de>
|
||||||
@@ -27,7 +27,16 @@
|
|||||||
#include <rtems/config.h>
|
#include <rtems/config.h>
|
||||||
#include <rtems/score/userextimpl.h>
|
#include <rtems/score/userextimpl.h>
|
||||||
|
|
||||||
CHAIN_DEFINE_EMPTY( _User_extensions_List );
|
#include <pthread.h>
|
||||||
|
|
||||||
|
User_extensions_List _User_extensions_List = {
|
||||||
|
CHAIN_INITIALIZER_EMPTY( _User_extensions_List.Active ),
|
||||||
|
CHAIN_ITERATOR_REGISTRY_INITIALIZER( _User_extensions_List.Iterators )
|
||||||
|
#if defined(RTEMS_SMP)
|
||||||
|
,
|
||||||
|
ISR_LOCK_INITIALIZER( "User Extensions List" )
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
void _User_extensions_Thread_create_visitor(
|
void _User_extensions_Thread_create_visitor(
|
||||||
Thread_Control *executing,
|
Thread_Control *executing,
|
||||||
@@ -139,17 +148,24 @@ void _User_extensions_Thread_terminate_visitor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void _User_extensions_Iterate(
|
void _User_extensions_Iterate(
|
||||||
void *arg,
|
void *arg,
|
||||||
User_extensions_Visitor visitor
|
User_extensions_Visitor visitor,
|
||||||
|
Chain_Iterator_direction direction
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
Thread_Control *executing = _Thread_Get_executing();
|
Thread_Control *executing;
|
||||||
const User_extensions_Table *callouts_current =
|
const User_extensions_Table *callouts_current;
|
||||||
rtems_configuration_get_user_extension_table();
|
const User_extensions_Table *callouts_end;
|
||||||
const User_extensions_Table *callouts_end =
|
const Chain_Node *end;
|
||||||
callouts_current + rtems_configuration_get_number_of_initial_extensions();
|
Chain_Node *node;
|
||||||
const Chain_Node *node;
|
User_extensions_Iterator iter;
|
||||||
const Chain_Node *tail;
|
ISR_lock_Context lock_context;
|
||||||
|
|
||||||
|
executing = _Thread_Get_executing();
|
||||||
|
|
||||||
|
callouts_current = rtems_configuration_get_user_extension_table();
|
||||||
|
callouts_end = callouts_current
|
||||||
|
+ rtems_configuration_get_number_of_initial_extensions();
|
||||||
|
|
||||||
while ( callouts_current != callouts_end ) {
|
while ( callouts_current != callouts_end ) {
|
||||||
(*visitor)( executing, arg, callouts_current );
|
(*visitor)( executing, arg, callouts_current );
|
||||||
@@ -157,14 +173,44 @@ void _User_extensions_Iterate(
|
|||||||
++callouts_current;
|
++callouts_current;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = _Chain_Immutable_first( &_User_extensions_List );
|
if ( direction == CHAIN_ITERATOR_FORWARD ) {
|
||||||
tail = _Chain_Immutable_tail( &_User_extensions_List );
|
end = _Chain_Immutable_tail( &_User_extensions_List.Active );
|
||||||
while ( node != tail ) {
|
} else {
|
||||||
const User_extensions_Control *extension =
|
end = _Chain_Immutable_head( &_User_extensions_List.Active );
|
||||||
(const User_extensions_Control *) node;
|
|
||||||
|
|
||||||
(*visitor)( executing, arg, &extension->Callouts );
|
|
||||||
|
|
||||||
node = _Chain_Immutable_next( node );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_User_extensions_Acquire( &lock_context );
|
||||||
|
|
||||||
|
_Chain_Iterator_initialize(
|
||||||
|
&_User_extensions_List.Active,
|
||||||
|
&_User_extensions_List.Iterators,
|
||||||
|
&iter.Iterator,
|
||||||
|
direction
|
||||||
|
);
|
||||||
|
|
||||||
|
if ( executing != NULL ) {
|
||||||
|
iter.previous = executing->last_user_extensions_iterator;
|
||||||
|
executing->last_user_extensions_iterator = &iter;
|
||||||
|
}
|
||||||
|
|
||||||
|
while ( ( node = _Chain_Iterator_next( &iter.Iterator ) ) != end ) {
|
||||||
|
const User_extensions_Control *extension;
|
||||||
|
|
||||||
|
_Chain_Iterator_set_position( &iter.Iterator, node );
|
||||||
|
|
||||||
|
_User_extensions_Release( &lock_context );
|
||||||
|
|
||||||
|
extension = (const User_extensions_Control *) node;
|
||||||
|
( *visitor )( executing, arg, &extension->Callouts );
|
||||||
|
|
||||||
|
_User_extensions_Acquire( &lock_context );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( executing != NULL ) {
|
||||||
|
executing->last_user_extensions_iterator = iter.previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
_Chain_Iterator_destroy( &iter.Iterator );
|
||||||
|
|
||||||
|
_User_extensions_Release( &lock_context );
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,16 +20,21 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <rtems/score/userextimpl.h>
|
#include <rtems/score/userextimpl.h>
|
||||||
#include <rtems/score/objectimpl.h>
|
|
||||||
#include <rtems/score/percpu.h>
|
#include <rtems/score/percpu.h>
|
||||||
|
|
||||||
void _User_extensions_Remove_set (
|
void _User_extensions_Remove_set (
|
||||||
User_extensions_Control *the_extension
|
User_extensions_Control *the_extension
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
_Assert( _Objects_Allocator_is_owner() );
|
ISR_lock_Context lock_context;
|
||||||
|
|
||||||
|
_User_extensions_Acquire( &lock_context );
|
||||||
|
_Chain_Iterator_registry_update(
|
||||||
|
&_User_extensions_List.Iterators,
|
||||||
|
&the_extension->Node
|
||||||
|
);
|
||||||
_Chain_Extract_unprotected( &the_extension->Node );
|
_Chain_Extract_unprotected( &the_extension->Node );
|
||||||
|
_User_extensions_Release( &lock_context );
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If a switch handler is present, remove it.
|
* If a switch handler is present, remove it.
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ _SUBDIRS = \
|
|||||||
spsignal_err01 spport_err01 spmsgq_err01 spmsgq_err02 spsem_err01 \
|
spsignal_err01 spport_err01 spmsgq_err01 spmsgq_err02 spsem_err01 \
|
||||||
spsem_err02 sptask_err01 spevent_err03 sptask_err03 sptask_err02 \
|
spsem_err02 sptask_err01 spevent_err03 sptask_err03 sptask_err02 \
|
||||||
sptask_err04 spclock_err01
|
sptask_err04 spclock_err01
|
||||||
|
_SUBDIRS += spextensions01
|
||||||
_SUBDIRS += spsysinit01
|
_SUBDIRS += spsysinit01
|
||||||
if HAS_SMP
|
if HAS_SMP
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ AM_CONDITIONAL(HAS_SMP,test "$rtems_cv_RTEMS_SMP" = "yes")
|
|||||||
|
|
||||||
# Explicitly list all Makefiles here
|
# Explicitly list all Makefiles here
|
||||||
AC_CONFIG_FILES([Makefile
|
AC_CONFIG_FILES([Makefile
|
||||||
|
spextensions01/Makefile
|
||||||
sptimerserver01/Makefile
|
sptimerserver01/Makefile
|
||||||
spsysinit01/Makefile
|
spsysinit01/Makefile
|
||||||
splinkersets01/Makefile
|
splinkersets01/Makefile
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
rtems_tests_PROGRAMS = spextensions01
|
||||||
|
spextensions01_SOURCES = init.c
|
||||||
|
|
||||||
|
dist_rtems_tests_DATA = spextensions01.scn spextensions01.doc
|
||||||
|
|
||||||
|
include $(RTEMS_ROOT)/make/custom/@RTEMS_BSP@.cfg
|
||||||
|
include $(top_srcdir)/../automake/compile.am
|
||||||
|
include $(top_srcdir)/../automake/leaf.am
|
||||||
|
|
||||||
|
AM_CPPFLAGS += -I$(top_srcdir)/../support/include
|
||||||
|
|
||||||
|
LINK_OBJS = $(spextensions01_OBJECTS)
|
||||||
|
LINK_LIBS = $(spextensions01_LDLIBS)
|
||||||
|
|
||||||
|
spextensions01$(EXEEXT): $(spextensions01_OBJECTS) $(spextensions01_DEPENDENCIES)
|
||||||
|
@rm -f spextensions01$(EXEEXT)
|
||||||
|
$(make-exe)
|
||||||
|
|
||||||
|
include $(top_srcdir)/../automake/local.am
|
||||||
@@ -0,0 +1,447 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016 embedded brains GmbH. All rights reserved.
|
||||||
|
*
|
||||||
|
* embedded brains GmbH
|
||||||
|
* Dornierstr. 4
|
||||||
|
* 82178 Puchheim
|
||||||
|
* Germany
|
||||||
|
* <rtems@embedded-brains.de>
|
||||||
|
*
|
||||||
|
* The license and distribution terms for this file may be
|
||||||
|
* found in the file LICENSE in this distribution or at
|
||||||
|
* http://www.rtems.com/license/LICENSE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <rtems/test.h>
|
||||||
|
|
||||||
|
#include <bsp.h>
|
||||||
|
|
||||||
|
const char rtems_test_name[] = "SPEXTENSIONS 1";
|
||||||
|
|
||||||
|
static int counter;
|
||||||
|
|
||||||
|
static int active_extensions = 2;
|
||||||
|
|
||||||
|
static rtems_id master_task;
|
||||||
|
|
||||||
|
static void assert_static_order(int index)
|
||||||
|
{
|
||||||
|
assert((counter % active_extensions) == index);
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assert_forward_order(int index)
|
||||||
|
{
|
||||||
|
assert((counter % active_extensions) == index);
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void assert_reverse_order(int index)
|
||||||
|
{
|
||||||
|
assert((counter % active_extensions) == (5 - index));
|
||||||
|
++counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool zero_thread_create(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_start(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_restart(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_delete(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_switch(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_begin(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_exitted(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_fatal(
|
||||||
|
rtems_fatal_source source,
|
||||||
|
bool is_internal,
|
||||||
|
rtems_fatal_code code
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (source == RTEMS_FATAL_SOURCE_EXIT) {
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void zero_thread_terminate(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool one_thread_create(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_start(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_restart(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_delete(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_switch(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_begin(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_exitted(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_fatal(
|
||||||
|
rtems_fatal_source source,
|
||||||
|
bool is_internal,
|
||||||
|
rtems_fatal_code code
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (source == RTEMS_FATAL_SOURCE_EXIT) {
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void one_thread_terminate(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_static_order(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool two_thread_create(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_start(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_restart(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_delete(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_reverse_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_switch(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_begin(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_exitted(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_fatal(
|
||||||
|
rtems_fatal_source source,
|
||||||
|
bool is_internal,
|
||||||
|
rtems_fatal_code code
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (source == RTEMS_FATAL_SOURCE_EXIT) {
|
||||||
|
assert_reverse_order(2);
|
||||||
|
assert(counter == 72);
|
||||||
|
rtems_test_endk();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void two_thread_terminate(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool three_thread_create(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_start(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_restart(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_static_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_delete(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_reverse_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_switch(rtems_tcb *a, rtems_tcb *b)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_begin(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_exitted(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_fatal(
|
||||||
|
rtems_fatal_source source,
|
||||||
|
bool is_internal,
|
||||||
|
rtems_fatal_code code
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if (source == RTEMS_FATAL_SOURCE_EXIT) {
|
||||||
|
assert_reverse_order(3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void three_thread_terminate(rtems_tcb *a)
|
||||||
|
{
|
||||||
|
assert_forward_order(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ZERO \
|
||||||
|
{ \
|
||||||
|
.thread_create = zero_thread_create, \
|
||||||
|
.thread_start = zero_thread_start, \
|
||||||
|
.thread_restart = zero_thread_restart, \
|
||||||
|
.thread_delete = zero_thread_delete, \
|
||||||
|
.thread_switch = zero_thread_switch, \
|
||||||
|
.thread_begin = zero_thread_begin, \
|
||||||
|
.thread_exitted = zero_thread_exitted, \
|
||||||
|
.fatal = zero_fatal, \
|
||||||
|
.thread_terminate = zero_thread_terminate \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ONE \
|
||||||
|
{ \
|
||||||
|
.thread_create = one_thread_create, \
|
||||||
|
.thread_start = one_thread_start, \
|
||||||
|
.thread_restart = one_thread_restart, \
|
||||||
|
.thread_delete = one_thread_delete, \
|
||||||
|
.thread_switch = one_thread_switch, \
|
||||||
|
.thread_begin = one_thread_begin, \
|
||||||
|
.thread_exitted = one_thread_exitted, \
|
||||||
|
.fatal = one_fatal, \
|
||||||
|
.thread_terminate = one_thread_terminate \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const rtems_extensions_table two = {
|
||||||
|
.thread_create = two_thread_create,
|
||||||
|
.thread_start = two_thread_start,
|
||||||
|
.thread_restart = two_thread_restart,
|
||||||
|
.thread_delete = two_thread_delete,
|
||||||
|
.thread_switch = two_thread_switch,
|
||||||
|
.thread_begin = two_thread_begin,
|
||||||
|
.thread_exitted = two_thread_exitted,
|
||||||
|
.fatal = two_fatal,
|
||||||
|
.thread_terminate = two_thread_terminate
|
||||||
|
};
|
||||||
|
|
||||||
|
static const rtems_extensions_table three = {
|
||||||
|
.thread_create = three_thread_create,
|
||||||
|
.thread_start = three_thread_start,
|
||||||
|
.thread_restart = three_thread_restart,
|
||||||
|
.thread_delete = three_thread_delete,
|
||||||
|
.thread_switch = three_thread_switch,
|
||||||
|
.thread_begin = three_thread_begin,
|
||||||
|
.thread_exitted = three_thread_exitted,
|
||||||
|
.fatal = three_fatal,
|
||||||
|
.thread_terminate = three_thread_terminate
|
||||||
|
};
|
||||||
|
|
||||||
|
static const rtems_extensions_table initial_test =
|
||||||
|
RTEMS_TEST_INITIAL_EXTENSION;
|
||||||
|
|
||||||
|
#ifdef BSP_INITIAL_EXTENSION
|
||||||
|
static const rtems_extensions_table initial_bsp =
|
||||||
|
BSP_INITIAL_EXTENSION;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void wake_up_master(void)
|
||||||
|
{
|
||||||
|
rtems_status_code sc;
|
||||||
|
|
||||||
|
sc = rtems_event_transient_send(master_task);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wait_for_worker(void)
|
||||||
|
{
|
||||||
|
rtems_status_code sc;
|
||||||
|
|
||||||
|
sc = rtems_event_transient_receive(
|
||||||
|
RTEMS_WAIT,
|
||||||
|
RTEMS_NO_TIMEOUT
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void worker(rtems_task_argument arg)
|
||||||
|
{
|
||||||
|
wake_up_master();
|
||||||
|
|
||||||
|
(void) rtems_task_suspend(RTEMS_SELF);
|
||||||
|
assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test(void)
|
||||||
|
{
|
||||||
|
rtems_status_code sc;
|
||||||
|
rtems_id id;
|
||||||
|
|
||||||
|
master_task = rtems_task_self();
|
||||||
|
|
||||||
|
#ifdef BSP_INITIAL_EXTENSION
|
||||||
|
sc = rtems_extension_create(
|
||||||
|
rtems_build_name(' ', 'B', 'S', 'P'),
|
||||||
|
&initial_bsp,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
#undef BSP_INITIAL_EXTENSION
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sc = rtems_extension_create(
|
||||||
|
rtems_build_name('T', 'E', 'S', 'T'),
|
||||||
|
&initial_test,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
sc = rtems_extension_create(
|
||||||
|
rtems_build_name('2', ' ', ' ', ' '),
|
||||||
|
&two,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
sc = rtems_extension_create(
|
||||||
|
rtems_build_name('3', ' ', ' ', ' '),
|
||||||
|
&three,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
active_extensions = 4;
|
||||||
|
assert(counter == 14);
|
||||||
|
counter = 16;
|
||||||
|
|
||||||
|
sc = rtems_task_create(
|
||||||
|
rtems_build_name('W', 'O', 'R', 'K'),
|
||||||
|
2,
|
||||||
|
RTEMS_MINIMUM_STACK_SIZE,
|
||||||
|
RTEMS_DEFAULT_MODES,
|
||||||
|
RTEMS_DEFAULT_ATTRIBUTES,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
sc = rtems_task_start(id, worker, 0);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
wait_for_worker();
|
||||||
|
|
||||||
|
sc = rtems_task_restart(id, 0);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
wait_for_worker();
|
||||||
|
|
||||||
|
sc = rtems_task_delete(id);
|
||||||
|
assert(sc == RTEMS_SUCCESSFUL);
|
||||||
|
|
||||||
|
/* Process zombies to trigger delete extensions */
|
||||||
|
sc = rtems_task_create(
|
||||||
|
rtems_build_name('N', 'U', 'L', 'L'),
|
||||||
|
2,
|
||||||
|
SIZE_MAX,
|
||||||
|
RTEMS_DEFAULT_MODES,
|
||||||
|
RTEMS_DEFAULT_ATTRIBUTES,
|
||||||
|
&id
|
||||||
|
);
|
||||||
|
assert(sc == RTEMS_UNSATISFIED);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Init(rtems_task_argument arg)
|
||||||
|
{
|
||||||
|
rtems_test_begink();
|
||||||
|
|
||||||
|
test();
|
||||||
|
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
|
||||||
|
#define CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
|
||||||
|
|
||||||
|
#define CONFIGURE_MAXIMUM_USER_EXTENSIONS 4
|
||||||
|
|
||||||
|
#define CONFIGURE_MAXIMUM_TASKS 2
|
||||||
|
|
||||||
|
#define CONFIGURE_INITIAL_EXTENSIONS ZERO, ONE
|
||||||
|
|
||||||
|
#define CONFIGURE_RTEMS_INIT_TASKS_TABLE
|
||||||
|
|
||||||
|
#define CONFIGURE_INIT
|
||||||
|
|
||||||
|
#include <rtems/confdefs.h>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
This file describes the directives and concepts tested by this test set.
|
||||||
|
|
||||||
|
test set name: spextensions01
|
||||||
|
|
||||||
|
directives:
|
||||||
|
|
||||||
|
- rtems_extension_create()
|
||||||
|
- _User_extensions_Iterate()
|
||||||
|
|
||||||
|
concepts:
|
||||||
|
|
||||||
|
- Ensure that the user extensions are called in the right order.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
*** BEGIN OF TEST SPEXTENSIONS 1 ***
|
||||||
|
*** END OF TEST SPEXTENSIONS 1 ***
|
||||||
Reference in New Issue
Block a user