source: rtems/cpukit/score/src/threadrestart.c @ 439c494

4.115
Last change on this file since 439c494 was 3f5f2ce, checked in by Sebastian Huber <sebastian.huber@…>, on 03/28/14 at 12:44:18

score: PR788: Add INTERNAL_ERROR_RESOURCE_IN_USE

Issue a fatal error in case a thread is deleted which still owns
resources (e.g. a binary semaphore with priority inheritance or ceiling
protocol). The resource count must be checked quite late since RTEMS
task variable destructors, POSIX key destructors, POSIX cleanup handler,
the Newlib thread termination extension or other thread termination
extensions may release resources. In this context it would be quite
difficult to return an error status to the caller.

An alternative would be to place threads with a non-zero resource count
not on the zombie chain. Thus we have a resource leak instead of a
fatal error. The terminator thread can see this error if we return an
RTEMS_RESOURCE_IN_USE status for the rtems_task_delete() for example.

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/**
2 * @file
3 *
4 * @brief Restart Thread
5 * @ingroup ScoreThread
6 */
7
8/*
9 *  COPYRIGHT (c) 1989-1999.
10 *  On-Line Applications Research Corporation (OAR).
11 *
12 *  Copyright (c) 2014 embedded brains GmbH.
13 *
14 *  The license and distribution terms for this file may be
15 *  found in the file LICENSE in this distribution or at
16 *  http://www.rtems.org/license/LICENSE.
17 */
18
19#if HAVE_CONFIG_H
20#include "config.h"
21#endif
22
23#include <rtems/score/threadimpl.h>
24#include <rtems/score/apimutex.h>
25#include <rtems/score/assert.h>
26#include <rtems/score/chainimpl.h>
27#include <rtems/score/isrlock.h>
28#include <rtems/score/schedulerimpl.h>
29#include <rtems/score/sysstate.h>
30#include <rtems/score/threadqimpl.h>
31#include <rtems/score/userextimpl.h>
32#include <rtems/score/watchdogimpl.h>
33#include <rtems/score/wkspace.h>
34
35typedef struct {
36  Chain_Control Chain;
37  ISR_lock_Control Lock;
38} Thread_Zombie_control;
39
40static Thread_Zombie_control _Thread_Zombies = {
41  .Chain = CHAIN_INITIALIZER_EMPTY( _Thread_Zombies.Chain ),
42  .Lock = ISR_LOCK_INITIALIZER( "thread zombies" )
43};
44
45static void _Thread_Make_zombie( Thread_Control *the_thread )
46{
47  ISR_lock_Context lock_context;
48  Thread_Zombie_control *zombies = &_Thread_Zombies;
49
50  if ( the_thread->resource_count != 0 ) {
51    _Terminate(
52      INTERNAL_ERROR_CORE,
53      false,
54      INTERNAL_ERROR_RESOURCE_IN_USE
55    );
56  }
57
58  _Thread_Set_state( the_thread, STATES_ZOMBIE );
59  _Thread_queue_Extract_with_proxy( the_thread );
60  _Watchdog_Remove( &the_thread->Timer );
61
62  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
63  _Chain_Append_unprotected( &zombies->Chain, &the_thread->Object.Node );
64  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
65}
66
67static void _Thread_Free( Thread_Control *the_thread )
68{
69  _User_extensions_Thread_delete( the_thread );
70
71  /*
72   * Free the per-thread scheduling information.
73   */
74  _Scheduler_Free( the_thread );
75
76  /*
77   *  The thread might have been FP.  So deal with that.
78   */
79#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
80#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE )
81  if ( _Thread_Is_allocated_fp( the_thread ) )
82    _Thread_Deallocate_fp();
83#endif
84
85  _Workspace_Free( the_thread->Start.fp_context );
86#endif
87
88  /*
89   *  Free the rest of the memory associated with this task
90   *  and set the associated pointers to NULL for safety.
91   */
92  _Thread_Stack_Free( the_thread );
93
94  _Workspace_Free( the_thread->extensions );
95
96  _Workspace_Free( the_thread->Start.tls_area );
97
98  _Objects_Free(
99    _Objects_Get_information_id( the_thread->Object.id ),
100    &the_thread->Object
101  );
102}
103
104static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
105{
106#if defined(RTEMS_SMP)
107  /*
108   * It is very unlikely that we see an executing thread here.  It can happen
109   * in case the thread termination sequence is interrupted by a slow interrupt
110   * service on a remote processor.
111   */
112  while (the_thread->is_executing) {
113    /* Wait */
114  }
115#else
116  (void) the_thread;
117#endif
118}
119
120void _Thread_Kill_zombies( void )
121{
122  ISR_lock_Context lock_context;
123  Thread_Zombie_control *zombies = &_Thread_Zombies;
124  Thread_Control *the_thread;
125
126  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
127
128  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
129  while ( the_thread != NULL ) {
130    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
131
132    _Thread_Wait_for_execution_stop( the_thread );
133    _Thread_Free( the_thread );
134
135    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
136
137    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
138  }
139
140  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
141}
142
143static void _Thread_Start_life_change_for_executing(
144  Thread_Control *executing
145)
146{
147  _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
148  _Assert(
149    executing->current_state == STATES_READY
150      || executing->current_state == STATES_SUSPENDED
151  );
152
153  _Thread_Add_post_switch_action( executing, &executing->Life.Action );
154}
155
156void _Thread_Life_action_handler(
157  Thread_Control  *executing,
158  Thread_Action   *action,
159  Per_CPU_Control *cpu,
160  ISR_Level        level
161)
162{
163  Thread_Life_state previous_life_state;
164
165  (void) action;
166
167  previous_life_state = executing->Life.state;
168  executing->Life.state = THREAD_LIFE_PROTECTED;
169
170  _Thread_Action_release_and_ISR_enable( cpu, level );
171
172  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
173    _User_extensions_Thread_terminate( executing );
174  } else {
175    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
176
177    _User_extensions_Thread_restart( executing );
178  }
179
180  _Thread_Disable_dispatch();
181
182  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
183    _Thread_Make_zombie( executing );
184
185    if ( executing->Life.terminator != NULL ) {
186      _Thread_Clear_state(
187        executing->Life.terminator,
188        STATES_WAITING_FOR_TERMINATION
189      );
190    }
191
192    _Thread_Enable_dispatch();
193
194    _Assert_Not_reached();
195  } else {
196    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
197
198    if ( _Thread_Is_life_terminating( executing->Life.state ) ) {
199      /* Someone deleted us in the mean-time */
200      _Thread_Start_life_change_for_executing( executing );
201    } else {
202      _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
203      _Assert(
204        executing->current_state == STATES_READY
205          || executing->current_state == STATES_SUSPENDED
206      );
207
208      executing->Life.state = THREAD_LIFE_NORMAL;
209
210      _Thread_Load_environment( executing );
211      _Thread_Restart_self( executing );
212
213      _Assert_Not_reached();
214    }
215  }
216}
217
218static void _Thread_Start_life_change(
219  Thread_Control   *the_thread,
220  Priority_Control  priority
221)
222{
223  the_thread->is_preemptible   = the_thread->Start.is_preemptible;
224  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
225  the_thread->budget_callout   = the_thread->Start.budget_callout;
226  the_thread->real_priority    = priority;
227
228  _Thread_Set_transient( the_thread );
229  _Thread_queue_Extract_with_proxy( the_thread );
230  _Watchdog_Remove( &the_thread->Timer );
231  _Scheduler_Set_priority_if_higher( the_thread, priority );
232  _Thread_Add_post_switch_action( the_thread, &the_thread->Life.Action );
233  _Thread_Ready( the_thread );
234  _Thread_Request_dispatch_if_executing( the_thread );
235}
236
237static void _Thread_Request_life_change(
238  Thread_Control    *the_thread,
239  Thread_Control    *executing,
240  Priority_Control   priority,
241  Thread_Life_state  additional_life_state
242)
243{
244  Thread_Life_state previous_life_state;
245  Per_CPU_Control *cpu;
246  ISR_Level level;
247
248  cpu = _Thread_Action_ISR_disable_and_acquire( the_thread, &level );
249  previous_life_state = the_thread->Life.state;
250  the_thread->Life.state = previous_life_state | additional_life_state;
251  _Thread_Action_release_and_ISR_enable( cpu, level );
252
253  if ( the_thread == executing ) {
254    executing->real_priority = priority;
255
256    _Scheduler_Set_priority_if_higher( the_thread, priority );
257    _Thread_Start_life_change_for_executing( executing );
258  } else if ( previous_life_state == THREAD_LIFE_NORMAL ) {
259    _Thread_Start_life_change( the_thread, priority );
260  } else {
261    _Thread_Clear_state( the_thread, STATES_SUSPENDED );
262
263    if ( _Thread_Is_life_terminating( additional_life_state ) ) {
264      the_thread->real_priority = _Scheduler_Highest_priority_of_two(
265        the_thread->real_priority,
266        priority
267      );
268
269      _Scheduler_Change_priority_if_higher( the_thread, priority, false );
270    }
271  }
272}
273
274void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
275{
276  _Assert( _Thread_Is_life_protected( executing->Life.state ) );
277
278  _Objects_Close(
279    _Objects_Get_information_id( the_thread->Object.id ),
280    &the_thread->Object
281  );
282
283  if ( _States_Is_dormant( the_thread->current_state ) ) {
284    _Thread_Make_zombie( the_thread );
285  } else {
286    if (
287      the_thread != executing
288        && !_Thread_Is_life_terminating( executing->Life.state )
289    ) {
290      /*
291       * Wait for termination of victim thread.  If the executing thread is
292       * also terminated, then do not wait.  This avoids potential cyclic
293       * dependencies and thus dead lock.
294       */
295       the_thread->Life.terminator = executing;
296       _Thread_Set_state( executing, STATES_WAITING_FOR_TERMINATION );
297    }
298
299    _Thread_Request_life_change(
300      the_thread,
301      executing,
302      executing->current_priority,
303      THREAD_LIFE_TERMINATING
304    );
305  }
306}
307
308bool _Thread_Restart(
309  Thread_Control            *the_thread,
310  Thread_Control            *executing,
311  void                      *pointer_argument,
312  Thread_Entry_numeric_type  numeric_argument
313)
314{
315  if ( !_States_Is_dormant( the_thread->current_state ) ) {
316    the_thread->Start.pointer_argument = pointer_argument;
317    the_thread->Start.numeric_argument = numeric_argument;
318
319    _Thread_Request_life_change(
320      the_thread,
321      executing,
322      the_thread->Start.initial_priority,
323      THREAD_LIFE_RESTARTING
324    );
325
326    return true;
327  }
328
329  return false;
330}
331
332bool _Thread_Set_life_protection( bool protect )
333{
334  bool previous_life_protection;
335  ISR_Level level;
336  Per_CPU_Control *cpu;
337  Thread_Control *executing;
338  Thread_Life_state previous_life_state;
339
340  cpu = _Thread_Action_ISR_disable_and_acquire_for_executing( &level );
341  executing = cpu->executing;
342
343  previous_life_state = executing->Life.state;
344  previous_life_protection = _Thread_Is_life_protected( previous_life_state );
345
346  if ( protect ) {
347    executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
348  } else {
349    executing->Life.state = previous_life_state & ~THREAD_LIFE_PROTECTED;
350  }
351
352  _Thread_Action_release_and_ISR_enable( cpu, level );
353
354#if defined(RTEMS_SMP)
355  /*
356   * On SMP configurations it is possible that a life change of an executing
357   * thread is requested, but this thread didn't notice it yet.  The life
358   * change is first marked in the life state field and then all scheduling and
359   * other thread state updates are performed.  The last step is to issues an
360   * inter-processor interrupt if necessary.  Since this takes some time we
361   * have to synchronize here.
362   */
363  if (
364    !_Thread_Is_life_protected( previous_life_state )
365      && _Thread_Is_life_changing( previous_life_state )
366  ) {
367    _Thread_Disable_dispatch();
368    _Thread_Enable_dispatch();
369
370    _Assert_Not_reached();
371  }
372#endif
373
374  if (
375    !protect
376      && _Thread_Is_life_changing( previous_life_state )
377  ) {
378    _Thread_Disable_dispatch();
379    _Thread_Start_life_change_for_executing( executing );
380    _Thread_Enable_dispatch();
381
382    _Assert_Not_reached();
383  }
384
385  return previous_life_protection;
386}
Note: See TracBrowser for help on using the repository browser.