From 1b1be254e7a3e3d6fe6d55d62010a81a7ef35411 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Tue, 25 Mar 2014 10:54:49 +0100 Subject: 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. --- cpukit/libcsupport/include/rtems/libcsupport.h | 7 +- cpukit/libcsupport/src/newlibc_reent.c | 13 +- cpukit/libcsupport/src/resource_snapshot.c | 5 +- cpukit/libmisc/monitor/mon-prmisc.c | 2 + cpukit/posix/include/rtems/posix/pthreadimpl.h | 4 +- cpukit/posix/src/keyrundestructors.c | 24 +- cpukit/posix/src/pthread.c | 22 +- cpukit/posix/src/pthreadexit.c | 82 +++---- cpukit/rtems/include/rtems/rtems/tasksimpl.h | 3 + cpukit/rtems/src/taskdelete.c | 37 +-- cpukit/rtems/src/tasks.c | 29 ++- cpukit/rtems/src/taskvariable_invoke_dtor.c | 2 +- cpukit/score/Makefile.am | 2 +- cpukit/score/include/rtems/score/statesimpl.h | 4 + cpukit/score/include/rtems/score/thread.h | 46 +++- cpukit/score/include/rtems/score/threadimpl.h | 56 ++++- cpukit/score/include/rtems/score/userext.h | 134 ++++++----- cpukit/score/include/rtems/score/userextimpl.h | 16 ++ cpukit/score/src/heapfree.c | 27 +-- cpukit/score/src/threadclose.c | 101 --------- cpukit/score/src/threadinitialize.c | 2 + cpukit/score/src/threadrestart.c | 299 ++++++++++++++++++++++++- cpukit/score/src/userextiterate.c | 14 ++ testsuites/psxtests/psxcleanup01/init.c | 9 + testsuites/smptests/smpthreadlife01/init.c | 72 +++++- testsuites/smptests/smpunsupported01/init.c | 3 - 26 files changed, 703 insertions(+), 312 deletions(-) delete mode 100644 cpukit/score/src/threadclose.c 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 #include -#include #include +#include +#include #include @@ -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 #include #include -#include +#include #include #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 +#include #include #include @@ -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 @@ -273,22 +273,31 @@ static void _POSIX_Threads_Delete_extension( Thread_Control *executing __attribute__((unused)), 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 #include +#include #include #include #include @@ -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 #include +#include #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 @@ -112,6 +112,17 @@ static void _RTEMS_tasks_Delete_extension( Thread_Control *executing, 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; @@ -119,25 +130,18 @@ static void _RTEMS_tasks_Delete_extension( * 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 -#include -#include -#include -#include -#include - -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 +#include +#include +#include +#include +#include +#include #include #include #include +#include + +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 +#include #include 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); -- cgit v1.2.3