source: rtems/cpukit/score/src/threadrestart.c @ c8e83288

4.115
Last change on this file since c8e83288 was c21c5912, checked in by Sebastian Huber <sebastian.huber@…>, on 05/14/14 at 09:11:05

score: Add STATES_RESTARTING

Use separate state for thread restart.

  • Property mode set to 100644
File size: 10.7 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( _Scheduler_Get( the_thread ), 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->Start.tls_area );
95
96  _Objects_Free(
97    _Objects_Get_information_id( the_thread->Object.id ),
98    &the_thread->Object
99  );
100}
101
102static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
103{
104#if defined(RTEMS_SMP)
105  /*
106   * It is very unlikely that we see an executing thread here.  It can happen
107   * in case the thread termination sequence is interrupted by a slow interrupt
108   * service on a remote processor.
109   */
110  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
111    /* Wait */
112  }
113#else
114  (void) the_thread;
115#endif
116}
117
118void _Thread_Kill_zombies( void )
119{
120  ISR_lock_Context lock_context;
121  Thread_Zombie_control *zombies = &_Thread_Zombies;
122  Thread_Control *the_thread;
123
124  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
125
126  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
127  while ( the_thread != NULL ) {
128    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
129
130    _Thread_Wait_for_execution_stop( the_thread );
131    _Thread_Free( the_thread );
132
133    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
134
135    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
136  }
137
138  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
139}
140
141static void _Thread_Start_life_change_for_executing(
142  Thread_Control *executing
143)
144{
145  _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
146  _Assert(
147    executing->current_state == STATES_READY
148      || executing->current_state == STATES_SUSPENDED
149  );
150
151  _Thread_Add_post_switch_action( executing, &executing->Life.Action );
152}
153
154void _Thread_Life_action_handler(
155  Thread_Control  *executing,
156  Thread_Action   *action,
157  Per_CPU_Control *cpu,
158  ISR_Level        level
159)
160{
161  Thread_Life_state previous_life_state;
162
163  (void) action;
164
165  previous_life_state = executing->Life.state;
166  executing->Life.state = THREAD_LIFE_PROTECTED;
167
168  _Thread_Action_release_and_ISR_enable( cpu, level );
169
170  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
171    _User_extensions_Thread_terminate( executing );
172  } else {
173    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
174
175    _User_extensions_Thread_restart( executing );
176  }
177
178  _Thread_Disable_dispatch();
179
180  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
181    _Thread_Make_zombie( executing );
182
183    if ( executing->Life.terminator != NULL ) {
184      _Thread_Clear_state(
185        executing->Life.terminator,
186        STATES_WAITING_FOR_TERMINATION
187      );
188    }
189
190    _Thread_Enable_dispatch();
191
192    _Assert_Not_reached();
193  } else {
194    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
195
196    if ( _Thread_Is_life_terminating( executing->Life.state ) ) {
197      /* Someone deleted us in the mean-time */
198      _Thread_Start_life_change_for_executing( executing );
199    } else {
200      _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
201      _Assert(
202        executing->current_state == STATES_READY
203          || executing->current_state == STATES_SUSPENDED
204      );
205
206      executing->Life.state = THREAD_LIFE_NORMAL;
207
208      _Thread_Load_environment( executing );
209      _Thread_Restart_self( executing );
210
211      _Assert_Not_reached();
212    }
213  }
214}
215
216static void _Thread_Start_life_change(
217  Thread_Control          *the_thread,
218  const Scheduler_Control *scheduler,
219  Priority_Control         priority
220)
221{
222  the_thread->is_preemptible   = the_thread->Start.is_preemptible;
223  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
224  the_thread->budget_callout   = the_thread->Start.budget_callout;
225  the_thread->real_priority    = priority;
226
227  _Thread_Set_state( the_thread, STATES_RESTARTING );
228  _Thread_queue_Extract_with_proxy( the_thread );
229  _Watchdog_Remove( &the_thread->Timer );
230  _Scheduler_Set_priority_if_higher( scheduler, the_thread, priority );
231  _Thread_Add_post_switch_action( the_thread, &the_thread->Life.Action );
232  _Thread_Ready( the_thread );
233  _Thread_Request_dispatch_if_executing( the_thread );
234}
235
236static void _Thread_Request_life_change(
237  Thread_Control    *the_thread,
238  Thread_Control    *executing,
239  Priority_Control   priority,
240  Thread_Life_state  additional_life_state
241)
242{
243  Thread_Life_state previous_life_state;
244  Per_CPU_Control *cpu;
245  ISR_Level level;
246  const Scheduler_Control *scheduler;
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  scheduler = _Scheduler_Get( the_thread );
254  if ( the_thread == executing ) {
255    executing->real_priority = priority;
256
257    _Scheduler_Set_priority_if_higher( scheduler, the_thread, priority );
258    _Thread_Start_life_change_for_executing( executing );
259  } else if ( previous_life_state == THREAD_LIFE_NORMAL ) {
260    _Thread_Start_life_change( the_thread, scheduler, priority );
261  } else {
262    _Thread_Clear_state( the_thread, STATES_SUSPENDED );
263
264    if ( _Thread_Is_life_terminating( additional_life_state ) ) {
265      the_thread->real_priority = _Scheduler_Highest_priority_of_two(
266        scheduler,
267        the_thread->real_priority,
268        priority
269      );
270
271      _Scheduler_Change_priority_if_higher(
272        scheduler,
273        the_thread,
274        priority,
275        false
276      );
277    }
278  }
279}
280
281void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
282{
283  _Assert( _Thread_Is_life_protected( executing->Life.state ) );
284
285  _Objects_Close(
286    _Objects_Get_information_id( the_thread->Object.id ),
287    &the_thread->Object
288  );
289
290  if ( _States_Is_dormant( the_thread->current_state ) ) {
291    _Thread_Make_zombie( the_thread );
292  } else {
293    if (
294      the_thread != executing
295        && !_Thread_Is_life_terminating( executing->Life.state )
296    ) {
297      /*
298       * Wait for termination of victim thread.  If the executing thread is
299       * also terminated, then do not wait.  This avoids potential cyclic
300       * dependencies and thus dead lock.
301       */
302       the_thread->Life.terminator = executing;
303       _Thread_Set_state( executing, STATES_WAITING_FOR_TERMINATION );
304    }
305
306    _Thread_Request_life_change(
307      the_thread,
308      executing,
309      executing->current_priority,
310      THREAD_LIFE_TERMINATING
311    );
312  }
313}
314
315bool _Thread_Restart(
316  Thread_Control            *the_thread,
317  Thread_Control            *executing,
318  void                      *pointer_argument,
319  Thread_Entry_numeric_type  numeric_argument
320)
321{
322  if ( !_States_Is_dormant( the_thread->current_state ) ) {
323    the_thread->Start.pointer_argument = pointer_argument;
324    the_thread->Start.numeric_argument = numeric_argument;
325
326    _Thread_Request_life_change(
327      the_thread,
328      executing,
329      the_thread->Start.initial_priority,
330      THREAD_LIFE_RESTARTING
331    );
332
333    return true;
334  }
335
336  return false;
337}
338
339bool _Thread_Set_life_protection( bool protect )
340{
341  bool previous_life_protection;
342  ISR_Level level;
343  Per_CPU_Control *cpu;
344  Thread_Control *executing;
345  Thread_Life_state previous_life_state;
346
347  cpu = _Thread_Action_ISR_disable_and_acquire_for_executing( &level );
348  executing = cpu->executing;
349
350  previous_life_state = executing->Life.state;
351  previous_life_protection = _Thread_Is_life_protected( previous_life_state );
352
353  if ( protect ) {
354    executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
355  } else {
356    executing->Life.state = previous_life_state & ~THREAD_LIFE_PROTECTED;
357  }
358
359  _Thread_Action_release_and_ISR_enable( cpu, level );
360
361#if defined(RTEMS_SMP)
362  /*
363   * On SMP configurations it is possible that a life change of an executing
364   * thread is requested, but this thread didn't notice it yet.  The life
365   * change is first marked in the life state field and then all scheduling and
366   * other thread state updates are performed.  The last step is to issues an
367   * inter-processor interrupt if necessary.  Since this takes some time we
368   * have to synchronize here.
369   */
370  if (
371    !_Thread_Is_life_protected( previous_life_state )
372      && _Thread_Is_life_changing( previous_life_state )
373  ) {
374    _Thread_Disable_dispatch();
375    _Thread_Enable_dispatch();
376
377    _Assert_Not_reached();
378  }
379#endif
380
381  if (
382    !protect
383      && _Thread_Is_life_changing( previous_life_state )
384  ) {
385    _Thread_Disable_dispatch();
386    _Thread_Start_life_change_for_executing( executing );
387    _Thread_Enable_dispatch();
388
389    _Assert_Not_reached();
390  }
391
392  return previous_life_protection;
393}
Note: See TracBrowser for help on using the repository browser.