diff options
Diffstat (limited to 'cpukit/score/src')
-rw-r--r-- | cpukit/score/src/heapfree.c | 27 | ||||
-rw-r--r-- | cpukit/score/src/threadclose.c | 101 | ||||
-rw-r--r-- | cpukit/score/src/threadinitialize.c | 2 | ||||
-rw-r--r-- | cpukit/score/src/threadrestart.c | 299 | ||||
-rw-r--r-- | cpukit/score/src/userextiterate.c | 14 |
5 files changed, 315 insertions, 128 deletions
diff --git a/cpukit/score/src/heapfree.c b/cpukit/score/src/heapfree.c index c45c294f3f..4e69146232 100644 --- a/cpukit/score/src/heapfree.c +++ b/cpukit/score/src/heapfree.c @@ -83,26 +83,13 @@ bool do_free = true; Heap_Block *const next = block->Protection_begin.next_delayed_free_block; - /* - * Sometimes after a free the allocated area is still in use. An example - * is the task stack of a thread that deletes itself. The thread dispatch - * disable level is a way to detect this use case. - */ - if ( _Thread_Dispatch_is_enabled() ) { - if ( next == NULL ) { - _Heap_Protection_delay_block_free( heap, block ); - do_free = false; - } else if ( next == HEAP_PROTECTION_OBOLUS ) { - _Heap_Protection_check_free_block( heap, block ); - } else { - _Heap_Protection_block_error( heap, block ); - } - } else if ( next == NULL ) { - /* - * This is a hack to prevent heavy workspace fragmentation which would - * lead to test suite failures. - */ - _Heap_Protection_free_all_delayed_blocks( heap ); + if ( next == NULL ) { + _Heap_Protection_delay_block_free( heap, block ); + do_free = false; + } else if ( next == HEAP_PROTECTION_OBOLUS ) { + _Heap_Protection_check_free_block( heap, block ); + } else { + _Heap_Protection_block_error( heap, block ); } return do_free; diff --git a/cpukit/score/src/threadclose.c b/cpukit/score/src/threadclose.c deleted file mode 100644 index 12896f5fee..0000000000 --- a/cpukit/score/src/threadclose.c +++ /dev/null @@ -1,101 +0,0 @@ -/** - * @file - * - * @brief Thread Close - * @ingroup ScoreThread - */ - -/* - * COPYRIGHT (c) 1989-2011. - * On-Line Applications Research Corporation (OAR). - * - * The license and distribution terms for this file may be - * found in the file LICENSE in this distribution or at - * http://www.rtems.org/license/LICENSE. - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include <rtems/score/threadimpl.h> -#include <rtems/score/schedulerimpl.h> -#include <rtems/score/threadqimpl.h> -#include <rtems/score/userextimpl.h> -#include <rtems/score/watchdogimpl.h> -#include <rtems/score/wkspace.h> - -void _Thread_Close( - Objects_Information *information, - Thread_Control *the_thread -) -{ - /* - * Now we are in a dispatching critical section again and we - * can take the thread OUT of the published set. It is invalid - * to use this thread's Id after this call. This will prevent - * any other task from attempting to initiate a call on this task. - */ - _Objects_Invalidate_Id( information, &the_thread->Object ); - - /* - * We assume the Allocator Mutex is locked when we get here. - * This provides sufficient protection to let the user extensions - * run but as soon as we get back, we will make the thread - * disappear and set a transient state on it. So we temporarily - * unnest dispatching. - */ - _Thread_Unnest_dispatch(); - - _User_extensions_Thread_delete( the_thread ); - - _Thread_Disable_dispatch(); - - /* - * Now we are in a dispatching critical section again and we - * can take the thread OUT of the published set. It is invalid - * to use this thread's Id OR name after this call. - */ - _Objects_Close( information, &the_thread->Object ); - - /* - * By setting the dormant state, the thread will not be considered - * for scheduling when we remove any blocking states. - */ - _Thread_Set_state( the_thread, STATES_DORMANT ); - - if ( !_Thread_queue_Extract_with_proxy( the_thread ) ) { - if ( _Watchdog_Is_active( &the_thread->Timer ) ) - (void) _Watchdog_Remove( &the_thread->Timer ); - } - - /* - * Free the per-thread scheduling information. - */ - _Scheduler_Free( the_thread ); - - /* - * The thread might have been FP. So deal with that. - */ -#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE ) -#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE ) - if ( _Thread_Is_allocated_fp( the_thread ) ) - _Thread_Deallocate_fp(); -#endif - the_thread->fp_context = NULL; - - _Workspace_Free( the_thread->Start.fp_context ); -#endif - - /* - * Free the rest of the memory associated with this task - * and set the associated pointers to NULL for safety. - */ - _Thread_Stack_Free( the_thread ); - the_thread->Start.stack = NULL; - - _Workspace_Free( the_thread->extensions ); - the_thread->extensions = NULL; - - _Workspace_Free( the_thread->Start.tls_area ); -} diff --git a/cpukit/score/src/threadinitialize.c b/cpukit/score/src/threadinitialize.c index c851320936..fd52742c41 100644 --- a/cpukit/score/src/threadinitialize.c +++ b/cpukit/score/src/threadinitialize.c @@ -244,6 +244,8 @@ bool _Thread_Initialize( &the_thread->Life.Action, _Thread_Life_action_handler ); + the_thread->Life.state = THREAD_LIFE_NORMAL; + the_thread->Life.terminator = NULL; /* * Open the object diff --git a/cpukit/score/src/threadrestart.c b/cpukit/score/src/threadrestart.c index b8fbd2384f..985329f470 100644 --- a/cpukit/score/src/threadrestart.c +++ b/cpukit/score/src/threadrestart.c @@ -9,6 +9,8 @@ * COPYRIGHT (c) 1989-1999. * On-Line Applications Research Corporation (OAR). * + * Copyright (c) 2014 embedded brains GmbH. + * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. @@ -19,9 +21,129 @@ #endif #include <rtems/score/threadimpl.h> +#include <rtems/score/apimutex.h> +#include <rtems/score/assert.h> +#include <rtems/score/chainimpl.h> +#include <rtems/score/isrlock.h> +#include <rtems/score/schedulerimpl.h> +#include <rtems/score/sysstate.h> #include <rtems/score/threadqimpl.h> #include <rtems/score/userextimpl.h> #include <rtems/score/watchdogimpl.h> +#include <rtems/score/wkspace.h> + +typedef struct { + Chain_Control Chain; + ISR_lock_Control Lock; +} Thread_Zombie_control; + +static Thread_Zombie_control _Thread_Zombies = { + .Chain = CHAIN_INITIALIZER_EMPTY( _Thread_Zombies.Chain ), + .Lock = ISR_LOCK_INITIALIZER( "thread zombies" ) +}; + +static void _Thread_Make_zombie( Thread_Control *the_thread ) +{ + ISR_lock_Context lock_context; + Thread_Zombie_control *zombies = &_Thread_Zombies; + + _Thread_Set_state( the_thread, STATES_ZOMBIE ); + _Thread_queue_Extract_with_proxy( the_thread ); + _Watchdog_Remove( &the_thread->Timer ); + + _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context ); + _Chain_Append_unprotected( &zombies->Chain, &the_thread->Object.Node ); + _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context ); +} + +static void _Thread_Free( Thread_Control *the_thread ) +{ + _User_extensions_Thread_delete( the_thread ); + + /* + * Free the per-thread scheduling information. + */ + _Scheduler_Free( the_thread ); + + /* + * The thread might have been FP. So deal with that. + */ +#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE ) +#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE ) + if ( _Thread_Is_allocated_fp( the_thread ) ) + _Thread_Deallocate_fp(); +#endif + + _Workspace_Free( the_thread->Start.fp_context ); +#endif + + /* + * Free the rest of the memory associated with this task + * and set the associated pointers to NULL for safety. + */ + _Thread_Stack_Free( the_thread ); + + _Workspace_Free( the_thread->extensions ); + + _Workspace_Free( the_thread->Start.tls_area ); + + _Objects_Free( + _Objects_Get_information_id( the_thread->Object.id ), + &the_thread->Object + ); +} + +static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread ) +{ +#if defined(RTEMS_SMP) + /* + * It is very unlikely that we see an executing thread here. It can happen + * in case the thread termination sequence is interrupted by a slow interrupt + * service on a remote processor. + */ + while (the_thread->is_executing) { + /* Wait */ + } +#else + (void) the_thread; +#endif +} + +void _Thread_Kill_zombies( void ) +{ + ISR_lock_Context lock_context; + Thread_Zombie_control *zombies = &_Thread_Zombies; + Thread_Control *the_thread; + + _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context ); + + the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain ); + while ( the_thread != NULL ) { + _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context ); + + _Thread_Wait_for_execution_stop( the_thread ); + _Thread_Free( the_thread ); + + _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context ); + + the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain ); + } + + _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context ); +} + +static void _Thread_Start_life_change_for_executing( + Thread_Control *executing +) +{ + _Assert( executing->Timer.state == WATCHDOG_INACTIVE ); + _Assert( + executing->current_state == STATES_READY + || executing->current_state == STATES_SUSPENDED + ); + + _Thread_Add_post_switch_action( executing, &executing->Life.Action ); +} void _Thread_Life_action_handler( Thread_Control *executing, @@ -30,15 +152,59 @@ void _Thread_Life_action_handler( ISR_Level level ) { + Thread_Life_state previous_life_state; + (void) action; + + previous_life_state = executing->Life.state; + executing->Life.state = THREAD_LIFE_PROTECTED; + _Thread_Action_release_and_ISR_enable( cpu, level ); - _User_extensions_Thread_restart( the_thread ); + if ( _Thread_Is_life_terminating( previous_life_state ) ) { + _User_extensions_Thread_terminate( executing ); + } else { + _Assert( _Thread_Is_life_restarting( previous_life_state ) ); + + _User_extensions_Thread_restart( executing ); + } _Thread_Disable_dispatch(); - _Thread_Load_environment( executing ); - _Thread_Restart_self( executing ); + if ( _Thread_Is_life_terminating( previous_life_state ) ) { + _Thread_Make_zombie( executing ); + + if ( executing->Life.terminator != NULL ) { + _Thread_Clear_state( + executing->Life.terminator, + STATES_WAITING_FOR_TERMINATION + ); + } + + _Thread_Enable_dispatch(); + + _Assert_Not_reached(); + } else { + _Assert( _Thread_Is_life_restarting( previous_life_state ) ); + + if ( _Thread_Is_life_terminating( executing->Life.state ) ) { + /* Someone deleted us in the mean-time */ + _Thread_Start_life_change_for_executing( executing ); + } else { + _Assert( executing->Timer.state == WATCHDOG_INACTIVE ); + _Assert( + executing->current_state == STATES_READY + || executing->current_state == STATES_SUSPENDED + ); + + executing->Life.state = THREAD_LIFE_NORMAL; + + _Thread_Load_environment( executing ); + _Thread_Restart_self( executing ); + + _Assert_Not_reached(); + } + } } static void _Thread_Start_life_change( @@ -55,7 +221,7 @@ static void _Thread_Start_life_change( _Thread_Set_transient( the_thread ); _Thread_queue_Extract_with_proxy( the_thread ); _Watchdog_Remove( &the_thread->Timer ); - _Thread_Set_priority( the_thread, priority ); + _Scheduler_Set_priority_if_higher( the_thread, priority ); _Thread_Add_post_switch_action( the_thread, &the_thread->Life.Action ); _Thread_Ready( the_thread ); _Thread_Request_dispatch_if_executing( the_thread ); @@ -64,10 +230,72 @@ static void _Thread_Start_life_change( static void _Thread_Request_life_change( Thread_Control *the_thread, Thread_Control *executing, - Priority_Control priority + Priority_Control priority, + Thread_Life_state additional_life_state ) { - _Thread_Start_life_change( the_thread, priority ); + Thread_Life_state previous_life_state; + Per_CPU_Control *cpu; + ISR_Level level; + + cpu = _Thread_Action_ISR_disable_and_acquire( the_thread, &level ); + previous_life_state = the_thread->Life.state; + the_thread->Life.state = previous_life_state | additional_life_state; + _Thread_Action_release_and_ISR_enable( cpu, level ); + + if ( the_thread == executing ) { + executing->real_priority = priority; + + _Scheduler_Set_priority_if_higher( the_thread, priority ); + _Thread_Start_life_change_for_executing( executing ); + } else if ( previous_life_state == THREAD_LIFE_NORMAL ) { + _Thread_Start_life_change( the_thread, priority ); + } else { + _Thread_Clear_state( the_thread, STATES_SUSPENDED ); + + if ( _Thread_Is_life_terminating( additional_life_state ) ) { + the_thread->real_priority = _Scheduler_Highest_priority_of_two( + the_thread->real_priority, + priority + ); + + _Scheduler_Change_priority_if_higher( the_thread, priority, false ); + } + } +} + +void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing ) +{ + _Assert( _Thread_Is_life_protected( executing->Life.state ) ); + + _Objects_Close( + _Objects_Get_information_id( the_thread->Object.id ), + &the_thread->Object + ); + + if ( _States_Is_dormant( the_thread->current_state ) ) { + _Thread_Make_zombie( the_thread ); + } else { + if ( + the_thread != executing + && !_Thread_Is_life_terminating( executing->Life.state ) + ) { + /* + * Wait for termination of victim thread. If the executing thread is + * also terminated, then do not wait. This avoids potential cyclic + * dependencies and thus dead lock. + */ + the_thread->Life.terminator = executing; + _Thread_Set_state( executing, STATES_WAITING_FOR_TERMINATION ); + } + + _Thread_Request_life_change( + the_thread, + executing, + executing->current_priority, + THREAD_LIFE_TERMINATING + ); + } } bool _Thread_Restart( @@ -84,7 +312,8 @@ bool _Thread_Restart( _Thread_Request_life_change( the_thread, executing, - the_thread->Start.initial_priority + the_thread->Start.initial_priority, + THREAD_LIFE_RESTARTING ); return true; @@ -92,3 +321,59 @@ bool _Thread_Restart( return false; } + +bool _Thread_Set_life_protection( bool protect ) +{ + bool previous_life_protection; + ISR_Level level; + Per_CPU_Control *cpu; + Thread_Control *executing; + Thread_Life_state previous_life_state; + + cpu = _Thread_Action_ISR_disable_and_acquire_for_executing( &level ); + executing = cpu->executing; + + previous_life_state = executing->Life.state; + previous_life_protection = _Thread_Is_life_protected( previous_life_state ); + + if ( protect ) { + executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED; + } else { + executing->Life.state = previous_life_state & ~THREAD_LIFE_PROTECTED; + } + + _Thread_Action_release_and_ISR_enable( cpu, level ); + +#if defined(RTEMS_SMP) + /* + * On SMP configurations it is possible that a life change of an executing + * thread is requested, but this thread didn't notice it yet. The life + * change is first marked in the life state field and then all scheduling and + * other thread state updates are performed. The last step is to issues an + * inter-processor interrupt if necessary. Since this takes some time we + * have to synchronize here. + */ + if ( + !_Thread_Is_life_protected( previous_life_state ) + && _Thread_Is_life_changing( previous_life_state ) + ) { + _Thread_Disable_dispatch(); + _Thread_Enable_dispatch(); + + _Assert_Not_reached(); + } +#endif + + if ( + !protect + && _Thread_Is_life_changing( previous_life_state ) + ) { + _Thread_Disable_dispatch(); + _Thread_Start_life_change_for_executing( executing ); + _Thread_Enable_dispatch(); + + _Assert_Not_reached(); + } + + return previous_life_protection; +} diff --git a/cpukit/score/src/userextiterate.c b/cpukit/score/src/userextiterate.c index c9125016de..beeee9570b 100644 --- a/cpukit/score/src/userextiterate.c +++ b/cpukit/score/src/userextiterate.c @@ -124,6 +124,20 @@ void _User_extensions_Fatal_visitor( } } +void _User_extensions_Thread_terminate_visitor( + Thread_Control *executing, + void *arg, + const User_extensions_Table *callouts +) +{ + User_extensions_thread_terminate_extension callout = + callouts->thread_terminate; + + if ( callout != NULL ) { + (*callout)( executing ); + } +} + void _User_extensions_Iterate( void *arg, User_extensions_Visitor visitor |