diff options
author | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2014-03-25 10:54:49 +0100 |
---|---|---|
committer | Sebastian Huber <sebastian.huber@embedded-brains.de> | 2014-03-31 08:29:43 +0200 |
commit | 1b1be254e7a3e3d6fe6d55d62010a81a7ef35411 (patch) | |
tree | c8bacf15b0f092728fd69debcb2387b65db33ed0 | |
parent | score: Replace _Thread_Reset() (diff) | |
download | rtems-1b1be254e7a3e3d6fe6d55d62010a81a7ef35411.tar.bz2 |
score: Thread life cycle re-implementation
The thread deletion is now supported on SMP.
This change fixes the following PRs:
PR1814: SMP race condition between stack free and dispatch
PR2035: psxcancel reveals NULL pointer access in _Thread_queue_Extract()
The POSIX cleanup handler are now called in the right context (should be
called in the context of the terminating thread).
http://pubs.opengroup.org/onlinepubs/009695399/functions/xsh_chap02_09.html
Add a user extension the reflects a thread termination event. This is
used to reclaim the Newlib reentrancy structure (may use file
operations), the POSIX cleanup handlers and the POSIX key destructors.
26 files changed, 703 insertions, 312 deletions
diff --git a/cpukit/libcsupport/include/rtems/libcsupport.h b/cpukit/libcsupport/include/rtems/libcsupport.h index 8e56e4d250..7d40084ece 100644 --- a/cpukit/libcsupport/include/rtems/libcsupport.h +++ b/cpukit/libcsupport/include/rtems/libcsupport.h @@ -90,6 +90,10 @@ void newlib_delete_hook( rtems_tcb *deleted_task ); +void newlib_terminate_hook( + rtems_tcb *current_task +); + #define RTEMS_NEWLIB_EXTENSION \ { \ newlib_create_hook, /* rtems_task_create */ \ @@ -99,7 +103,8 @@ void newlib_delete_hook( 0, /* task_switch */ \ __RTEMS_NEWLIB_BEGIN, /* task_begin */ \ 0, /* task_exitted */ \ - 0 /* fatal */ \ + 0, /* fatal */ \ + newlib_terminate_hook /* thread terminate */ \ } typedef struct { diff --git a/cpukit/libcsupport/src/newlibc_reent.c b/cpukit/libcsupport/src/newlibc_reent.c index 88e5e183f8..cd3ac2a0ec 100644 --- a/cpukit/libcsupport/src/newlibc_reent.c +++ b/cpukit/libcsupport/src/newlibc_reent.c @@ -66,13 +66,16 @@ void newlib_delete_hook( rtems_tcb *deleted_task ) { - struct _reent *ptr; + (void) current_task; - ptr = deleted_task->libc_reent; - deleted_task->libc_reent = NULL; + _Workspace_Free(deleted_task->libc_reent); +} - _reclaim_reent(ptr); - _Workspace_Free(ptr); +void newlib_terminate_hook( + rtems_tcb *current_task +) +{ + _reclaim_reent(current_task->libc_reent); } #endif diff --git a/cpukit/libcsupport/src/resource_snapshot.c b/cpukit/libcsupport/src/resource_snapshot.c index 8ebbbdc84a..8c66f0f7c2 100644 --- a/cpukit/libcsupport/src/resource_snapshot.c +++ b/cpukit/libcsupport/src/resource_snapshot.c @@ -22,8 +22,9 @@ #include <rtems/libio_.h> #include <rtems/malloc.h> -#include <rtems/score/wkspace.h> #include <rtems/score/protectedheap.h> +#include <rtems/score/threadimpl.h> +#include <rtems/score/wkspace.h> #include <rtems/extensionimpl.h> @@ -116,6 +117,8 @@ void rtems_resource_snapshot_take(rtems_resource_snapshot *snapshot) free_all_delayed_blocks(); + _Thread_Kill_zombies(); + _Protected_heap_Get_information(RTEMS_Malloc_Heap, &snapshot->heap_info); _Thread_Disable_dispatch(); diff --git a/cpukit/libmisc/monitor/mon-prmisc.c b/cpukit/libmisc/monitor/mon-prmisc.c index 75c9ecf7eb..1d6fef592f 100644 --- a/cpukit/libmisc/monitor/mon-prmisc.c +++ b/cpukit/libmisc/monitor/mon-prmisc.c @@ -135,6 +135,8 @@ static const rtems_assoc_t rtems_monitor_state_assoc[] = { { "Wrwlk", STATES_WAITING_FOR_RWLOCK, 0 }, { "Wisig", STATES_INTERRUPTIBLE_BY_SIGNAL, 0 }, { "Wwkup", STATES_WAITING_FOR_BSD_WAKEUP, 0 }, + { "Wterm", STATES_WAITING_FOR_TERMINATION, 0 }, + { "ZOMBI", STATES_ZOMBIE, 0 }, { 0, 0, 0 }, }; diff --git a/cpukit/posix/include/rtems/posix/pthreadimpl.h b/cpukit/posix/include/rtems/posix/pthreadimpl.h index af68990d6b..02d8bca0f5 100644 --- a/cpukit/posix/include/rtems/posix/pthreadimpl.h +++ b/cpukit/posix/include/rtems/posix/pthreadimpl.h @@ -23,7 +23,7 @@ #include <rtems/posix/config.h> #include <rtems/posix/threadsup.h> #include <rtems/score/objectimpl.h> -#include <rtems/score/thread.h> +#include <rtems/score/threadimpl.h> #include <rtems/score/assert.h> #ifdef __cplusplus @@ -217,6 +217,8 @@ int rtems_pthread_attribute_compare( RTEMS_INLINE_ROUTINE Thread_Control *_POSIX_Threads_Allocate( void ) { + _Thread_Kill_zombies(); + return (Thread_Control *) _Objects_Allocate( &_POSIX_Threads_Information ); } diff --git a/cpukit/posix/src/keyrundestructors.c b/cpukit/posix/src/keyrundestructors.c index 762e2e6f85..96147a539d 100644 --- a/cpukit/posix/src/keyrundestructors.c +++ b/cpukit/posix/src/keyrundestructors.c @@ -7,7 +7,7 @@ /* * Copyright (c) 2012 Zhongwei Yao. - * Copyright (c) 2010 embedded brains GmbH. + * Copyright (c) 2010-2014 embedded brains GmbH. * * COPYRIGHT (c) 1989-2014. * On-Line Applications Research Corporation (OAR). @@ -22,6 +22,7 @@ #endif #include <rtems/posix/keyimpl.h> +#include <rtems/score/assert.h> #include <rtems/score/chainimpl.h> #include <rtems/score/thread.h> @@ -44,14 +45,15 @@ void _POSIX_Keys_Run_destructors( POSIX_Keys_Control *the_key; Objects_Locations location; - _Thread_Disable_dispatch(); - chain = &thread->Key_Chain; iter = (POSIX_Keys_Key_value_pair *) _Chain_First( chain ); while ( !_Chain_Is_tail( chain, &iter->Key_values_per_thread_node ) ) { next = (POSIX_Keys_Key_value_pair *) _Chain_Next( &iter->Key_values_per_thread_node ); + the_key = _POSIX_Keys_Get( iter->key, &location ); + _Assert( location == OBJECTS_LOCAL ); + /** * remove key from rbtree and chain. * here Chain_Node *iter can be convert to POSIX_Keys_Key_value_pair *, @@ -64,21 +66,19 @@ void _POSIX_Keys_Run_destructors( ); _Chain_Extract_unprotected( &iter->Key_values_per_thread_node ); - /** - * run key value's destructor if destructor and value are both non-null. - */ - the_key = _POSIX_Keys_Get( iter->key, &location ); destructor = the_key->destructor; value = iter->value; - if ( destructor != NULL && value != NULL ) - (*destructor)( value ); + + _POSIX_Keys_Key_value_pair_free( iter ); _Objects_Put( &the_key->Object ); - _POSIX_Keys_Key_value_pair_free( iter ); + /** + * run key value's destructor if destructor and value are both non-null. + */ + if ( destructor != NULL && value != NULL ) + (*destructor)( value ); iter = next; } - - _Thread_Enable_dispatch(); } diff --git a/cpukit/posix/src/pthread.c b/cpukit/posix/src/pthread.c index 0416e28adf..8d1a8eb209 100644 --- a/cpukit/posix/src/pthread.c +++ b/cpukit/posix/src/pthread.c @@ -274,21 +274,30 @@ static void _POSIX_Threads_Delete_extension( Thread_Control *deleted ) { + _Workspace_Free( deleted->API_Extensions[ THREAD_API_POSIX ] ); +} + +static void _POSIX_Threads_Terminate_extension( + Thread_Control *executing +) +{ Thread_Control *the_thread; POSIX_API_Control *api; void **value_ptr; - api = deleted->API_Extensions[ THREAD_API_POSIX ]; + api = executing->API_Extensions[ THREAD_API_POSIX ]; /* * Run the POSIX cancellation handlers */ - _POSIX_Threads_cancel_run( deleted ); + _POSIX_Threads_cancel_run( executing ); + + _Thread_Disable_dispatch(); /* * Wakeup all the tasks which joined with this one */ - value_ptr = (void **) deleted->Wait.return_argument; + value_ptr = (void **) executing->Wait.return_argument; while ( (the_thread = _Thread_queue_Dequeue( &api->Join_List )) ) *(void **)the_thread->Wait.return_argument = value_ptr; @@ -296,9 +305,7 @@ static void _POSIX_Threads_Delete_extension( if ( api->schedpolicy == SCHED_SPORADIC ) (void) _Watchdog_Remove( &api->Sporadic_timer ); - deleted->API_Extensions[ THREAD_API_POSIX ] = NULL; - - _Workspace_Free( api ); + _Thread_Enable_dispatch(); } /* @@ -350,7 +357,8 @@ User_extensions_Control _POSIX_Threads_User_extensions = { NULL, /* switch */ NULL, /* begin */ _POSIX_Threads_Exitted_extension, /* exitted */ - NULL /* fatal */ + NULL, /* fatal */ + _POSIX_Threads_Terminate_extension /* terminate */ } }; diff --git a/cpukit/posix/src/pthreadexit.c b/cpukit/posix/src/pthreadexit.c index 8ffb54eb5a..40cd16023a 100644 --- a/cpukit/posix/src/pthreadexit.c +++ b/cpukit/posix/src/pthreadexit.c @@ -21,6 +21,7 @@ #include <pthread.h> #include <rtems/posix/pthreadimpl.h> +#include <rtems/score/assert.h> #include <rtems/score/apimutex.h> #include <rtems/score/threadimpl.h> #include <rtems/score/threadqimpl.h> @@ -30,65 +31,46 @@ void _POSIX_Thread_Exit( void *value_ptr ) { - Objects_Information *the_information; - Thread_Control *unblocked; - POSIX_API_Control *api; - - the_information = _Objects_Get_information_id( the_thread->Object.id ); + Thread_Control *unblocked; + POSIX_API_Control *api; + bool previous_life_protection; api = the_thread->API_Extensions[ THREAD_API_POSIX ]; + _Assert( _Debug_Is_thread_dispatching_allowed() ); - /* - * The_information has to be non-NULL. Otherwise, we couldn't be - * running in a thread of this API and class. - * - * NOTE: Lock and unlock in different order so we do not throw a - * fatal error when locking the allocator mutex. And after - * we unlock, we want to defer the context switch until we - * are ready to be switched out. Otherwise, an ISR could - * occur and preempt us out while we still hold the - * allocator mutex. - */ - - _RTEMS_Lock_allocator(); - _Thread_Disable_dispatch(); - - the_thread->Wait.return_argument = value_ptr; + previous_life_protection = _Thread_Set_life_protection( true ); + _Thread_Disable_dispatch(); - /* - * Process join - */ - if ( api->detachstate == PTHREAD_CREATE_JOINABLE ) { - unblocked = _Thread_queue_Dequeue( &api->Join_List ); - if ( unblocked ) { - do { - *(void **)unblocked->Wait.return_argument = value_ptr; - } while ( (unblocked = _Thread_queue_Dequeue( &api->Join_List )) ); - } else { - _Thread_Set_state( - the_thread, - STATES_WAITING_FOR_JOIN_AT_EXIT | STATES_TRANSIENT - ); - /* FIXME: Lock order reversal */ - _RTEMS_Unlock_allocator(); - _Thread_Enable_dispatch(); - /* now waiting for thread to arrive */ - _RTEMS_Lock_allocator(); - _Thread_Disable_dispatch(); - } - } + the_thread->Wait.return_argument = value_ptr; - /* - * Now shut down the thread - */ - _Thread_Close( the_information, the_thread ); + /* + * Process join + */ + if ( api->detachstate == PTHREAD_CREATE_JOINABLE ) { + unblocked = _Thread_queue_Dequeue( &api->Join_List ); + if ( unblocked ) { + do { + *(void **)unblocked->Wait.return_argument = value_ptr; + } while ( (unblocked = _Thread_queue_Dequeue( &api->Join_List )) ); + } else { + _Thread_Set_state( + the_thread, + STATES_WAITING_FOR_JOIN_AT_EXIT | STATES_TRANSIENT + ); + _Thread_Enable_dispatch(); + /* now waiting for thread to arrive */ + _Thread_Disable_dispatch(); + } + } - _POSIX_Threads_Free( the_thread ); + /* + * Now shut down the thread + */ + _Thread_Close( the_thread, _Thread_Executing ); - /* FIXME: Lock order reversal */ - _RTEMS_Unlock_allocator(); _Thread_Enable_dispatch(); + _Thread_Set_life_protection( previous_life_protection ); } void pthread_exit( diff --git a/cpukit/rtems/include/rtems/rtems/tasksimpl.h b/cpukit/rtems/include/rtems/rtems/tasksimpl.h index fbee00e316..2a99812e26 100644 --- a/cpukit/rtems/include/rtems/rtems/tasksimpl.h +++ b/cpukit/rtems/include/rtems/rtems/tasksimpl.h @@ -19,6 +19,7 @@ #include <rtems/rtems/tasks.h> #include <rtems/score/objectimpl.h> +#include <rtems/score/threadimpl.h> #ifdef __cplusplus extern "C" { @@ -84,6 +85,8 @@ void _RTEMS_Tasks_Invoke_task_variable_dtor( */ RTEMS_INLINE_ROUTINE Thread_Control *_RTEMS_tasks_Allocate( void ) { + _Thread_Kill_zombies(); + return (Thread_Control *) _Objects_Allocate( &_RTEMS_tasks_Information ); } diff --git a/cpukit/rtems/src/taskdelete.c b/cpukit/rtems/src/taskdelete.c index b92f5f2a7f..dc9a2a1961 100644 --- a/cpukit/rtems/src/taskdelete.c +++ b/cpukit/rtems/src/taskdelete.c @@ -27,32 +27,15 @@ rtems_status_code rtems_task_delete( rtems_id id ) { - Thread_Control *the_thread; - Objects_Locations location; - Objects_Information *the_information; - -#if defined( RTEMS_SMP ) - if ( rtems_configuration_is_smp_enabled() ) { - return RTEMS_NOT_IMPLEMENTED; - } -#endif - - _RTEMS_Lock_allocator(); + Thread_Control *the_thread; + Objects_Locations location; + bool previous_life_protection; + previous_life_protection = _Thread_Set_life_protection( true ); the_thread = _Thread_Get( id, &location ); switch ( location ) { case OBJECTS_LOCAL: - the_information = _Objects_Get_information_id( the_thread->Object.id ); - - #if defined(RTEMS_DEBUG) - if ( !the_information ) { - _Objects_Put( &the_thread->Object ); - return RTEMS_INVALID_ID; - /* This should never happen if _Thread_Get() works right */ - } - #endif - #if defined(RTEMS_MULTIPROCESSING) if ( the_thread->is_global ) { _Objects_MP_Close( &_RTEMS_tasks_Information, the_thread->Object.id ); @@ -64,19 +47,16 @@ rtems_status_code rtems_task_delete( } #endif - _Thread_Close( the_information, the_thread ); + _Thread_Close( the_thread, _Thread_Executing ); - _RTEMS_tasks_Free( the_thread ); - - /* FIXME: Lock order reversal */ - _RTEMS_Unlock_allocator(); _Objects_Put( &the_thread->Object ); + _Thread_Set_life_protection( previous_life_protection ); return RTEMS_SUCCESSFUL; #if defined(RTEMS_MULTIPROCESSING) case OBJECTS_REMOTE: - _RTEMS_Unlock_allocator(); _Thread_Dispatch(); + _Thread_Set_life_protection( previous_life_protection ); return RTEMS_ILLEGAL_ON_REMOTE_OBJECT; #endif @@ -84,6 +64,7 @@ rtems_status_code rtems_task_delete( break; } - _RTEMS_Unlock_allocator(); + _Thread_Set_life_protection( previous_life_protection ); + return RTEMS_INVALID_ID; } diff --git a/cpukit/rtems/src/tasks.c b/cpukit/rtems/src/tasks.c index c859d248b5..594695080a 100644 --- a/cpukit/rtems/src/tasks.c +++ b/cpukit/rtems/src/tasks.c @@ -113,31 +113,35 @@ static void _RTEMS_tasks_Delete_extension( Thread_Control *deleted ) { + /* + * Free API specific memory + */ + + (void) _Workspace_Free( deleted->API_Extensions[ THREAD_API_RTEMS ] ); +} + +static void _RTEMS_tasks_Terminate_extension( + Thread_Control *executing +) +{ rtems_task_variable_t *tvp, *next; /* * Free per task variable memory */ - tvp = deleted->task_variables; - deleted->task_variables = NULL; + tvp = executing->task_variables; + executing->task_variables = NULL; while (tvp) { next = (rtems_task_variable_t *)tvp->next; - _RTEMS_Tasks_Invoke_task_variable_dtor( deleted, tvp ); + _RTEMS_Tasks_Invoke_task_variable_dtor( executing, tvp ); tvp = next; } /* * Run all the key destructors */ - _POSIX_Keys_Run_destructors( deleted ); - - /* - * Free API specific memory - */ - - (void) _Workspace_Free( deleted->API_Extensions[ THREAD_API_RTEMS ] ); - deleted->API_Extensions[ THREAD_API_RTEMS ] = NULL; + _POSIX_Keys_Run_destructors( executing ); } /* @@ -189,7 +193,8 @@ User_extensions_Control _RTEMS_tasks_User_extensions = { _RTEMS_tasks_Switch_extension, /* switch */ NULL, /* begin */ NULL, /* exitted */ - NULL /* fatal */ + NULL, /* fatal */ + _RTEMS_tasks_Terminate_extension /* terminate */ } }; diff --git a/cpukit/rtems/src/taskvariable_invoke_dtor.c b/cpukit/rtems/src/taskvariable_invoke_dtor.c index a23e5e56fa..51b9453d82 100644 --- a/cpukit/rtems/src/taskvariable_invoke_dtor.c +++ b/cpukit/rtems/src/taskvariable_invoke_dtor.c @@ -31,7 +31,7 @@ void _RTEMS_Tasks_Invoke_task_variable_dtor( void *value; dtor = tvp->dtor; - if (_Thread_Is_executing(the_thread)) { + if (_Thread_Get_executing() == the_thread) { value = *tvp->ptr; *tvp->ptr = tvp->gval; } else { diff --git a/cpukit/score/Makefile.am b/cpukit/score/Makefile.am index c6dd47b781..e2d23a4a69 100644 --- a/cpukit/score/Makefile.am +++ b/cpukit/score/Makefile.am @@ -272,7 +272,7 @@ libscore_a_SOURCES += src/rbtree.c \ ## THREAD_C_FILES libscore_a_SOURCES += src/thread.c src/threadchangepriority.c \ - src/threadclearstate.c src/threadclose.c src/threadcreateidle.c \ + src/threadclearstate.c src/threadcreateidle.c \ src/threaddelayended.c src/threaddispatch.c \ src/threadenabledispatch.c src/threaddisabledispatch.c \ src/threadget.c src/threadhandler.c src/threadinitialize.c \ diff --git a/cpukit/score/include/rtems/score/statesimpl.h b/cpukit/score/include/rtems/score/statesimpl.h index 913922b30c..842d108236 100644 --- a/cpukit/score/include/rtems/score/statesimpl.h +++ b/cpukit/score/include/rtems/score/statesimpl.h @@ -78,6 +78,10 @@ extern "C" { #define STATES_WAITING_FOR_SYSTEM_EVENT 0x40000 /** This macro corresponds to a task waiting for BSD wakeup. */ #define STATES_WAITING_FOR_BSD_WAKEUP 0x80000 +/** This macro corresponds to a task waiting for a task termination. */ +#define STATES_WAITING_FOR_TERMINATION 0x100000 +/** This macro corresponds to a task being a zombie. */ +#define STATES_ZOMBIE 0x200000 /** This macro corresponds to a task which is in an interruptible * blocking state. diff --git a/cpukit/score/include/rtems/score/thread.h b/cpukit/score/include/rtems/score/thread.h index d853aa035a..31fbbfa7d4 100644 --- a/cpukit/score/include/rtems/score/thread.h +++ b/cpukit/score/include/rtems/score/thread.h @@ -11,6 +11,8 @@ * COPYRIGHT (c) 1989-2014. * 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. @@ -396,8 +398,47 @@ typedef struct { Chain_Control Chain; } Thread_Action_control; +/** + * @brief Thread life states. + * + * The thread life states are orthogonal to the thread states used for + * synchronization primitives and blocking operations. They reflect the state + * changes triggered with thread restart and delete requests. + */ +typedef enum { + THREAD_LIFE_NORMAL = 0x0, + THREAD_LIFE_PROTECTED = 0x1, + THREAD_LIFE_RESTARTING = 0x2, + THREAD_LIFE_PROTECTED_RESTARTING = 0x3, + THREAD_LIFE_TERMINATING = 0x4, + THREAD_LIFE_PROTECTED_TERMINATING = 0x5, + THREAD_LIFE_RESTARTING_TERMINTING = 0x6, + THREAD_LIFE_PROTECTED_RESTARTING_TERMINTING = 0x7 +} Thread_Life_state; + +/** + * @brief Thread life control. + */ typedef struct { + /** + * @brief Thread life action used to react upon thread restart and delete + * requests. + */ Thread_Action Action; + + /** + * @brief The current thread life state. + */ + Thread_Life_state state; + + /** + * @brief The terminator thread of this thread. + * + * In case the thread is terminated and another thread (the terminator) waits + * for the actual termination completion, then this field references the + * terminator thread. + */ + Thread_Control *terminator; } Thread_Life_control; /** @@ -470,9 +511,10 @@ struct Thread_Control_struct { * thread and thread dispatching is necessary. On SMP a thread dispatch on a * remote processor needs help from an inter-processor interrupt, thus it * will take some time to complete the state change. A lot of things can - * happen in the meantime. + * happen in the meantime. This field is volatile since it is polled in + * _Thread_Kill_zombies(). */ - bool is_executing; + volatile bool is_executing; #if __RTEMS_HAVE_SYS_CPUSET_H__ /** diff --git a/cpukit/score/include/rtems/score/threadimpl.h b/cpukit/score/include/rtems/score/threadimpl.h index 2e31753817..d0c7933aa3 100644 --- a/cpukit/score/include/rtems/score/threadimpl.h +++ b/cpukit/score/include/rtems/score/threadimpl.h @@ -11,6 +11,8 @@ * COPYRIGHT (c) 1989-2008. * 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. @@ -194,6 +196,8 @@ bool _Thread_Restart( Thread_Entry_numeric_type numeric_argument ); +bool _Thread_Set_life_protection( bool protect ); + void _Thread_Life_action_handler( Thread_Control *executing, Thread_Action *action, @@ -202,16 +206,24 @@ void _Thread_Life_action_handler( ); /** - * @brief Frees all memory associated with the specified thread. + * @brief Kills all zombie threads in the system. * - * This routine frees all memory associated with the specified - * thread and removes it from the local object table so no further - * operations on this thread are allowed. + * Threads change into the zombie state as the last step in the thread + * termination sequence right before a context switch to the heir thread is + * initiated. Since the thread stack is still in use during this phase we have + * to postpone the thread stack reclamation until this point. On SMP + * configurations we may have to busy wait for context switch completion here. */ -void _Thread_Close( - Objects_Information *information, - Thread_Control *the_thread -); +void _Thread_Kill_zombies( void ); + +/** + * @brief Closes the thread. + * + * Closes the thread object and starts the thread termination sequence. In + * case the executing thread is not terminated, then this function waits until + * the terminating thread reached the zombie state. + */ +void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing ); /** * @brief Removes any set states for @a the_thread. @@ -710,6 +722,34 @@ RTEMS_INLINE_ROUTINE void _Thread_Add_post_switch_action( _Thread_Action_release_and_ISR_enable( cpu, level ); } +RTEMS_INLINE_ROUTINE bool _Thread_Is_life_restarting( + Thread_Life_state life_state +) +{ + return ( life_state & THREAD_LIFE_RESTARTING ) != 0; +} + +RTEMS_INLINE_ROUTINE bool _Thread_Is_life_terminating( + Thread_Life_state life_state +) +{ + return ( life_state & THREAD_LIFE_TERMINATING ) != 0; +} + +RTEMS_INLINE_ROUTINE bool _Thread_Is_life_protected( + Thread_Life_state life_state +) +{ + return ( life_state & THREAD_LIFE_PROTECTED ) != 0; +} + +RTEMS_INLINE_ROUTINE bool _Thread_Is_life_changing( + Thread_Life_state life_state +) +{ + return ( life_state & THREAD_LIFE_RESTARTING_TERMINTING ) != 0; +} + #if !defined(__DYNAMIC_REENT__) /** * This routine returns the C library re-enterant pointer. diff --git a/cpukit/score/include/rtems/score/userext.h b/cpukit/score/include/rtems/score/userext.h index 91e7f97bd3..2bd8f8a88b 100644 --- a/cpukit/score/include/rtems/score/userext.h +++ b/cpukit/score/include/rtems/score/userext.h @@ -43,9 +43,7 @@ typedef void User_extensions_routine RTEMS_COMPILER_DEPRECATED_ATTRIBUTE; * @brief Task create extension. * * It corresponds to _Thread_Initialize() (used by the rtems_task_create() - * directive). The first parameter points to the currently executing thread - * which created the new thread. The second parameter points to the created - * thread. + * directive and pthread_create()). * * It is invoked after the new thread has been completely initialized, but * before it is placed on a ready chain. @@ -59,140 +57,173 @@ typedef void User_extensions_routine RTEMS_COMPILER_DEPRECATED_ATTRIBUTE; * * It can be assumed that the executing thread locked the allocator mutex. * The only exception is the creation of the idle thread. In this case the - * allocator mutex is not locked. Since the allocator mutex is non-recursive, - * it is prohibited to call the normal memory allocation routines. It is - * possible to use internal rountines like _Workspace_Allocate() or - * _Heap_Allocate() for heaps which are protected by the allocator mutex. + * allocator mutex is not locked. Since the allocator mutex allows nesting the + * normal memory allocation routines can be used. + * + * @param[in] executing The executing thread. + * @param[in] created The created thread. * - * @retval true The thread create extension was successful. + * @retval true Successful operation. * @retval false A thread create user extension will frequently attempt to * allocate resources. If this allocation fails, then the extension should * return @a false and the entire thread create operation will fail. */ typedef bool ( *User_extensions_thread_create_extension )( - Thread_Control *, - Thread_Control * + Thread_Control *executing, + Thread_Control *created ); /** * @brief Task delete extension. * * It corresponds to _Thread_Close() (used by the rtems_task_delete() - * directive). The first parameter points to the currently executing thread - * which deleted the thread. The second parameter points to the deleted - * thread. + * directive, pthread_exit() and pthread_cancel()). * - * It is invoked before all resources of the thread are deleted. + * It is invoked before all resources of the thread are deleted. The executing + * and deleted arguments are never equal. * * Thread dispatching is enabled. The executing thread locked the allocator * mutex. + * + * @param[in] executing The executing thread. + * @param[in] deleted The deleted thread. */ typedef void( *User_extensions_thread_delete_extension )( - Thread_Control *, - Thread_Control * + Thread_Control *executing, + Thread_Control *deleted ); /** * @brief Task start extension. * * It corresponds to _Thread_Start() (used by the rtems_task_start() - * directive). The first parameter points to the currently executing thread - * which started the thread. The second parameter points to the started - * thread. + * directive). * * It is invoked after the environment of the thread has been loaded and the * thread has been made ready. * * Thread dispatching is disabled. The executing thread is not the holder of * the allocator mutex. + * + * @param[in] executing The executing thread. + * @param[in] started The started thread. */ typedef void( *User_extensions_thread_start_extension )( - Thread_Control *, - Thread_Control * + Thread_Control *executing, + Thread_Control *started ); /** * @brief Task restart extension. * * It corresponds to _Thread_Restart() (used by the rtems_task_restart() - * directive). The first parameter points to the currently executing thread - * which restarted the thread. The second parameter points to the restarted - * thread. + * directive). * * It is invoked in the context of the restarted thread right before the - * execution context is restarted. The executing and restarted arguments are - * equal. The thread stack reflects the previous execution context. + * execution context is reloaded. The executing and restarted arguments are + * always equal. The thread stack reflects the previous execution context. * * Thread dispatching is enabled. The thread is not the holder of the - * allocator mutex. + * allocator mutex. The thread life is protected. Thread restart and delete + * requests issued by restart extensions lead to recursion. + * + * @param[in] executing The executing thread. + * @param[in] restarted The executing thread. Yes, the executing thread. */ typedef void( *User_extensions_thread_restart_extension )( - Thread_Control *, - Thread_Control * + Thread_Control *executing, + Thread_Control *restarted ); /** * @brief Task switch extension. * - * It corresponds to _Thread_Dispatch(). The first parameter points to the - * currently executing thread. The second parameter points to the heir thread. + * It corresponds to _Thread_Dispatch(). * * It is invoked before the context switch from the executing to the heir * thread. * * Thread dispatching is disabled. The state of the allocator mutex is - * arbitrary. + * arbitrary. Interrupts are disabled and the per-CPU lock is acquired on SMP + * configurations. * - * The context switches initiated through _Thread_Start_multitasking() and - * _Thread_Stop_multitasking() are not covered by this extension. The - * executing thread may run with a minimal setup, for example with a freed task - * stack. + * The context switches initiated through _Thread_Start_multitasking() are not + * covered by this extension. + * + * @param[in] executing The executing thread. + * @param[in] heir The heir thread. */ typedef void( *User_extensions_thread_switch_extension )( - Thread_Control *, - Thread_Control * + Thread_Control *executing, + Thread_Control *heir ); /** * @brief Task begin extension. * - * It corresponds to _Thread_Handler(). The first parameter points to the - * currently executing thread which begins now execution. + * It corresponds to _Thread_Handler(). * * Thread dispatching is disabled. The executing thread is not the holder of * the allocator mutex. + * + * @param[in] executing The executing thread. */ typedef void( *User_extensions_thread_begin_extension )( - Thread_Control * + Thread_Control *executing ); /** * @brief Task exitted extension. * - * It corresponds to _Thread_Handler(). The first parameter points to the - * currently executing thread which exitted before. + * It corresponds to _Thread_Handler() after a return of the entry function. * * Thread dispatching is disabled. The state of the allocator mutex is * arbitrary. + * + * @param[in] executing The executing thread. */ typedef void( *User_extensions_thread_exitted_extension )( - Thread_Control * + Thread_Control *executing ); /** * @brief Fatal error extension. * - * It corresponds to _Terminate() (used by the - * rtems_fatal_error_occurred() directive). The first parameter contains the - * error source. The second parameter indicates if it was an internal error. - * The third parameter contains the error code. + * It corresponds to _Terminate() (used by the rtems_fatal() directive). * * This extension should not call any RTEMS directives. + * + * @param[in] source The fatal source indicating the subsystem the fatal + * condition originated in. + * @param[in] is_internal Indicates if the fatal condition was generated + * internally to the executive. + * @param[in] code The fatal error code. This value must be interpreted with + * respect to the source. */ typedef void( *User_extensions_fatal_extension )( - Internal_errors_Source, - bool, - Internal_errors_t + Internal_errors_Source source, + bool is_internal, + Internal_errors_t code +); + +/** + * @brief Task termination extension. + * + * This extension is invoked by _Thread_Life_action_handler() in case a + * termination request is recognized. + * + * It is invoked in the context of the terminated thread right before the + * thread dispatch to the heir thread. The POSIX cleanup and key destructors + * execute in this context. + * + * Thread dispatching is enabled. The thread is not the holder of the + * allocator mutex. The thread life is protected. Thread restart and delete + * requests issued by terminate extensions lead to recursion. + * + * @param[in] terminated The terminated thread. + */ +typedef void( *User_extensions_thread_terminate_extension )( + Thread_Control *terminated ); /** @@ -207,6 +238,7 @@ typedef struct { User_extensions_thread_begin_extension thread_begin; User_extensions_thread_exitted_extension thread_exitted; User_extensions_fatal_extension fatal; + User_extensions_thread_terminate_extension thread_terminate; } User_extensions_Table; /** diff --git a/cpukit/score/include/rtems/score/userextimpl.h b/cpukit/score/include/rtems/score/userextimpl.h index 25c6f00d0b..04808e1f17 100644 --- a/cpukit/score/include/rtems/score/userextimpl.h +++ b/cpukit/score/include/rtems/score/userextimpl.h @@ -142,6 +142,12 @@ void _User_extensions_Fatal_visitor( const User_extensions_Table *callouts ); +void _User_extensions_Thread_terminate_visitor( + Thread_Control *executing, + void *arg, + const User_extensions_Table *callouts +); + /** * @brief Iterates through all user extensions and calls the visitor for each. * @@ -239,6 +245,16 @@ static inline void _User_extensions_Fatal( _User_extensions_Iterate( &ctx, _User_extensions_Fatal_visitor ); } +static inline void _User_extensions_Thread_terminate( + Thread_Control *executing +) +{ + _User_extensions_Iterate( + executing, + _User_extensions_Thread_terminate_visitor + ); +} + /** @} */ /** @} */ 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 diff --git a/testsuites/psxtests/psxcleanup01/init.c b/testsuites/psxtests/psxcleanup01/init.c index d29a891f54..f38b48ee93 100644 --- a/testsuites/psxtests/psxcleanup01/init.c +++ b/testsuites/psxtests/psxcleanup01/init.c @@ -96,6 +96,15 @@ static void test_restart_with_cleanup(void) wait_for_restart_task(); rtems_test_assert(restart_cleanup_arg == 1); + + wait_for_restart_task(); + + sc = rtems_task_delete(id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + wait_for_restart_task(); + + rtems_test_assert(restart_cleanup_arg == 2); } static void cleaner(void *arg) diff --git a/testsuites/smptests/smpthreadlife01/init.c b/testsuites/smptests/smpthreadlife01/init.c index ee36068c73..83e742e16c 100644 --- a/testsuites/smptests/smpthreadlife01/init.c +++ b/testsuites/smptests/smpthreadlife01/init.c @@ -19,6 +19,7 @@ #include "tmacros.h" #include <rtems.h> +#include <rtems/libcsupport.h> #include <rtems/score/smpbarrier.h> const char rtems_test_name[] = "SMPTHREADLIFE 1"; @@ -28,6 +29,7 @@ const char rtems_test_name[] = "SMPTHREADLIFE 1"; typedef struct { volatile rtems_task_argument main_arg; volatile rtems_task_argument worker_arg; + volatile bool terminated; SMP_barrier_Control barrier; SMP_barrier_State worker_barrier_state; } test_context; @@ -37,6 +39,29 @@ static test_context test_instance = { .worker_barrier_state = SMP_BARRIER_STATE_INITIALIZER }; +static void restart_extension( + Thread_Control *executing, + Thread_Control *restarted +) +{ + rtems_test_assert(executing == restarted); +} + +static void delete_extension( + Thread_Control *executing, + Thread_Control *deleted +) +{ + rtems_test_assert(executing != deleted); +} + +static void terminate_extension(Thread_Control *executing) +{ + test_context *ctx = &test_instance; + + ctx->terminated = true; +} + static void worker_task(rtems_task_argument arg) { test_context *ctx = &test_instance; @@ -59,6 +84,9 @@ static void test(void) rtems_status_code sc; rtems_id id; rtems_task_argument arg; + rtems_resource_snapshot snapshot; + + rtems_resource_snapshot_take(&snapshot); sc = rtems_task_create( rtems_build_name('W', 'O', 'R', 'K'), @@ -90,6 +118,42 @@ static void test(void) rtems_test_assert(ctx->worker_arg == arg); } + + sc = rtems_task_delete(id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + rtems_test_assert(rtems_resource_snapshot_check(&snapshot)); + + for (arg = 31; arg < 57; ++arg) { + ctx->main_arg = arg; + ctx->worker_arg = 0; + ctx->terminated = false; + + sc = rtems_task_create( + rtems_build_name('W', 'O', 'R', 'K'), + 1, + RTEMS_MINIMUM_STACK_SIZE, + RTEMS_DEFAULT_MODES, + RTEMS_DEFAULT_ATTRIBUTES, + &id + ); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + sc = rtems_task_start(id, worker_task, arg); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + _SMP_barrier_Wait(&ctx->barrier, &barrier_state, CPU_COUNT); + + rtems_test_assert(ctx->worker_arg == arg); + rtems_test_assert(!ctx->terminated); + + sc = rtems_task_delete(id); + rtems_test_assert(sc == RTEMS_SUCCESSFUL); + + rtems_test_assert(ctx->terminated); + + rtems_test_assert(rtems_resource_snapshot_check(&snapshot)); + } } static void Init(rtems_task_argument arg) @@ -113,7 +177,13 @@ static void Init(rtems_task_argument arg) #define CONFIGURE_MAXIMUM_TASKS CPU_COUNT -#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION +#define CONFIGURE_INITIAL_EXTENSIONS \ + { \ + .thread_restart = restart_extension, \ + .thread_delete = delete_extension, \ + .thread_terminate = terminate_extension \ + }, \ + RTEMS_TEST_INITIAL_EXTENSION #define CONFIGURE_RTEMS_INIT_TASKS_TABLE diff --git a/testsuites/smptests/smpunsupported01/init.c b/testsuites/smptests/smpunsupported01/init.c index ffa40137aa..3a819b8f79 100644 --- a/testsuites/smptests/smpunsupported01/init.c +++ b/testsuites/smptests/smpunsupported01/init.c @@ -28,9 +28,6 @@ static void test(void) rtems_test_assert(rtems_configuration_is_smp_enabled()); - sc = rtems_task_delete(RTEMS_SELF); - rtems_test_assert(sc == RTEMS_NOT_IMPLEMENTED); - sc = rtems_task_variable_add(RTEMS_SELF, NULL, NULL); rtems_test_assert(sc == RTEMS_NOT_IMPLEMENTED); |