[PATCH 14/17] score: Thread life cycle re-implementation

Sebastian Huber sebastian.huber at embedded-brains.de
Tue Mar 25 12:49:18 UTC 2014


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  |   46 +++-
 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               |  303 +++++++++++++++++++++++-
 cpukit/score/src/userextiterate.c              |   14 +
 testsuites/psxtests/psxcleanup01/init.c        |    9 +
 testsuites/smptests/smpthreadlife01/init.c     |   64 +++++-
 testsuites/smptests/smpunsupported01/init.c    |    3 -
 26 files changed, 687 insertions(+), 314 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 8e56e4d..7d40084 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 88e5e18..cd3ac2a 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 8ebbbdc..8c66f0f 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 75c9ecf..1d6fef5 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 af68990..02d8bca 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 762e2e6..96147a5 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 0416e28..8d1a8eb 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 8ffb54e..40cd160 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 fbee00e..2a99812 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 b92f5f2..dc9a2a1 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 c859d24..5946950 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 a23e5e5..51b9453 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 c6dd47b..e2d23a4 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 913922b..842d108 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 33c69ad..67ce696 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.
@@ -412,8 +414,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_RESTARTED = 0x2,
+  THREAD_LIFE_PROTECTED_RESTARTED = 0x3,
+  THREAD_LIFE_TERMINATED = 0x4,
+  THREAD_LIFE_PROTECTED_TERMINATED = 0x5,
+  THREAD_LIFE_RESTARTED_TERMINATED = 0x6,
+  THREAD_LIFE_PROTECTED_RESTARTED_TERMINATED = 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;
 
 /**
@@ -486,9 +527,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 2e31753..9166e9d 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,
@@ -201,17 +205,9 @@ void _Thread_Life_action_handler(
   ISR_Level        level
 );
 
-/**
- *  @brief Frees all memory associated with the specified thread.
- *
- *  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.
- */
-void _Thread_Close(
-  Objects_Information  *information,
-  Thread_Control       *the_thread
-);
+void _Thread_Kill_zombies( void );
+
+void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing );
 
 /**
  *  @brief Removes any set states for @a the_thread.
@@ -710,6 +706,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_restarted(
+  Thread_Life_state life_state
+)
+{
+  return ( life_state & THREAD_LIFE_RESTARTED ) != 0;
+}
+
+RTEMS_INLINE_ROUTINE bool _Thread_Is_life_terminated(
+  Thread_Life_state life_state
+)
+{
+  return ( life_state & THREAD_LIFE_TERMINATED ) != 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_change_requested(
+  Thread_Life_state life_state
+)
+{
+  return ( life_state & THREAD_LIFE_RESTARTED_TERMINATED ) != 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 91e7f97..2bd8f8a 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 25c6f00..04808e1 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 c45c294..4e69146 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 12896f5..0000000
--- 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 c851320..fd52742 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 b8fbd23..57e948c 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_terminated( previous_life_state ) ) {
+    _User_extensions_Thread_terminate( executing );
+  } else {
+    _Assert( _Thread_Is_life_restarted( previous_life_state ) );
+
+    _User_extensions_Thread_restart( executing );
+  }
 
   _Thread_Disable_dispatch();
 
-  _Thread_Load_environment( executing );
-  _Thread_Restart_self( executing );
+  if ( _Thread_Is_life_terminated( 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_restarted( previous_life_state ) );
+
+    if ( _Thread_Is_life_terminated( executing->Life.state ) ) {
+      /* Someone deleted us 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(
@@ -64,10 +230,78 @@ 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;
+    _Thread_Set_priority( executing, 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_terminated( additional_life_state ) ) {
+      the_thread->real_priority = _Scheduler_Highest_priority_of_two(
+        the_thread->real_priority,
+        priority
+      );
+
+      if (
+        _Scheduler_Is_priority_higher_than(
+          priority,
+          the_thread->current_priority
+        )
+      ) {
+        _Thread_Change_priority( 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_terminated( 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_TERMINATED
+    );
+  }
 }
 
 bool _Thread_Restart(
@@ -84,7 +318,8 @@ bool _Thread_Restart(
     _Thread_Request_life_change(
       the_thread,
       executing,
-      the_thread->Start.initial_priority
+      the_thread->Start.initial_priority,
+      THREAD_LIFE_RESTARTED
     );
 
     return true;
@@ -92,3 +327,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_change_requested( previous_life_state )
+  ) {
+    _Thread_Disable_dispatch();
+    _Thread_Enable_dispatch();
+
+    _Assert_Not_reached();
+  }
+#endif
+
+  if (
+    !protect
+      && _Thread_Is_life_change_requested( 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 c912501..beeee95 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 d29a891..f38b48e 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 ee36068..9432bdd 100644
--- a/testsuites/smptests/smpthreadlife01/init.c
+++ b/testsuites/smptests/smpthreadlife01/init.c
@@ -28,6 +28,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 +38,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;
@@ -90,6 +114,38 @@ static void test(void)
 
     rtems_test_assert(ctx->worker_arg == arg);
   }
+
+  sc = rtems_task_delete(id);
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
+
+  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);
+  }
 }
 
 static void Init(rtems_task_argument arg)
@@ -113,7 +169,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 ffa4013..3a819b8 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);
 
-- 
1.7.7




More information about the devel mailing list