source: rtems/cpukit/score/src/threadrestart.c @ 5eaf0e7

5
Last change on this file since 5eaf0e7 was 5eaf0e7, checked in by Sebastian Huber <sebastian.huber@…>, on 03/17/16 at 06:56:31

posix: Use per-thread lookup tree for POSIX Keys

Yields higher performance on SMP systems.

Close #2625.

  • Property mode set to 100644
File size: 11.5 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 bool _Thread_Raise_real_priority_filter(
46  Thread_Control   *the_thread,
47  Priority_Control *new_priority_ptr,
48  void             *arg
49)
50{
51  Priority_Control real_priority;
52  Priority_Control new_priority;
53  Priority_Control current_priority;
54
55  real_priority = the_thread->real_priority;
56  new_priority = *new_priority_ptr;
57  current_priority = the_thread->current_priority;
58
59  new_priority = _Thread_Priority_highest( real_priority, new_priority );
60  *new_priority_ptr = new_priority;
61
62  the_thread->real_priority = new_priority;
63
64  return _Thread_Priority_less_than( current_priority, new_priority );
65}
66
67static void _Thread_Make_zombie( Thread_Control *the_thread )
68{
69  ISR_lock_Context lock_context;
70  Thread_Zombie_control *zombies = &_Thread_Zombies;
71
72  if ( _Thread_Owns_resources( the_thread ) ) {
73    _Terminate(
74      INTERNAL_ERROR_CORE,
75      false,
76      INTERNAL_ERROR_RESOURCE_IN_USE
77    );
78  }
79
80  _Objects_Close(
81    _Objects_Get_information_id( the_thread->Object.id ),
82    &the_thread->Object
83  );
84
85  _Thread_Set_state( the_thread, STATES_ZOMBIE );
86  _Thread_queue_Extract_with_proxy( the_thread );
87  _Thread_Timer_remove( the_thread );
88
89  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
90  _Chain_Append_unprotected( &zombies->Chain, &the_thread->Object.Node );
91  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
92}
93
94static void _Thread_Free( Thread_Control *the_thread )
95{
96  Thread_Information *information = (Thread_Information *)
97    _Objects_Get_information_id( the_thread->Object.id );
98
99  _User_extensions_Thread_delete( the_thread );
100  _ISR_lock_Destroy( &the_thread->Keys.Lock );
101  _Scheduler_Node_destroy( _Scheduler_Get( the_thread ), the_thread );
102  _ISR_lock_Destroy( &the_thread->Timer.Lock );
103
104  /*
105   *  The thread might have been FP.  So deal with that.
106   */
107#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
108#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE )
109  if ( _Thread_Is_allocated_fp( the_thread ) )
110    _Thread_Deallocate_fp();
111#endif
112
113  _Workspace_Free( the_thread->Start.fp_context );
114#endif
115
116  _Freechain_Put(
117    &information->Free_thread_queue_heads,
118    the_thread->Wait.spare_heads
119  );
120
121  /*
122   *  Free the rest of the memory associated with this task
123   *  and set the associated pointers to NULL for safety.
124   */
125  _Thread_Stack_Free( the_thread );
126
127  _Workspace_Free( the_thread->Start.tls_area );
128
129#if defined(RTEMS_SMP)
130  _SMP_ticket_lock_Destroy( &the_thread->Lock.Default );
131  _SMP_lock_Stats_destroy( &the_thread->Lock.Stats );
132  _SMP_lock_Stats_destroy( &the_thread->Potpourri_stats );
133#endif
134
135  _Objects_Free( &information->Objects, &the_thread->Object );
136}
137
138static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
139{
140#if defined(RTEMS_SMP)
141  /*
142   * It is very unlikely that we see an executing thread here.  It can happen
143   * in case the thread termination sequence is interrupted by a slow interrupt
144   * service on a remote processor.
145   */
146  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
147    /* Wait */
148  }
149#else
150  (void) the_thread;
151#endif
152}
153
154void _Thread_Kill_zombies( void )
155{
156  ISR_lock_Context lock_context;
157  Thread_Zombie_control *zombies = &_Thread_Zombies;
158  Thread_Control *the_thread;
159
160  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
161
162  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
163  while ( the_thread != NULL ) {
164    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
165
166    _Thread_Wait_for_execution_stop( the_thread );
167    _Thread_Free( the_thread );
168
169    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
170
171    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
172  }
173
174  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
175}
176
177static void _Thread_Add_life_change_action(
178  Thread_Control *the_thread
179)
180{
181  _Thread_Add_post_switch_action(
182    the_thread,
183    &the_thread->Life.Action,
184    _Thread_Life_action_handler
185  );
186}
187
188static void _Thread_Start_life_change_for_executing(
189  Thread_Control *executing
190)
191{
192  _Assert(
193    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
194  );
195  _Assert(
196    executing->current_state == STATES_READY
197      || executing->current_state == STATES_SUSPENDED
198  );
199
200  _Thread_Add_life_change_action( executing );
201}
202
203void _Thread_Life_action_handler(
204  Thread_Control  *executing,
205  Thread_Action   *action,
206  Per_CPU_Control *cpu,
207  ISR_Level        level
208)
209{
210  Thread_Life_state previous_life_state;
211
212  (void) action;
213
214  previous_life_state = executing->Life.state;
215  executing->Life.state = THREAD_LIFE_PROTECTED;
216
217  _Thread_Action_release_and_ISR_enable( cpu, level );
218
219  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
220    _User_extensions_Thread_terminate( executing );
221  } else {
222    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
223
224    _User_extensions_Thread_restart( executing );
225  }
226
227  _Thread_Disable_dispatch();
228
229  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
230    _Thread_Make_zombie( executing );
231
232    if ( executing->Life.terminator != NULL ) {
233      _Thread_Clear_state(
234        executing->Life.terminator,
235        STATES_WAITING_FOR_TERMINATION
236      );
237    }
238
239    _Thread_Enable_dispatch();
240
241    _Assert_Not_reached();
242  } else {
243    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
244
245    if ( _Thread_Is_life_terminating( executing->Life.state ) ) {
246      /* Someone deleted us in the mean-time */
247      _Thread_Start_life_change_for_executing( executing );
248    } else {
249      _Assert(
250        _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
251      );
252      _Assert(
253        executing->current_state == STATES_READY
254          || executing->current_state == STATES_SUSPENDED
255      );
256
257      executing->Life.state = THREAD_LIFE_NORMAL;
258
259      _Thread_Load_environment( executing );
260      _Thread_Restart_self( executing );
261
262      _Assert_Not_reached();
263    }
264  }
265}
266
267static void _Thread_Start_life_change(
268  Thread_Control          *the_thread,
269  const Scheduler_Control *scheduler,
270  Priority_Control         priority
271)
272{
273  the_thread->is_preemptible   = the_thread->Start.is_preemptible;
274  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
275  the_thread->budget_callout   = the_thread->Start.budget_callout;
276
277  _Thread_Set_state( the_thread, STATES_RESTARTING );
278  _Thread_queue_Extract_with_proxy( the_thread );
279  _Thread_Timer_remove( the_thread );
280  _Thread_Change_priority(
281    the_thread,
282    priority,
283    NULL,
284    _Thread_Raise_real_priority_filter,
285    false
286  );
287  _Thread_Add_life_change_action( the_thread );
288  _Thread_Ready( the_thread );
289}
290
291static void _Thread_Request_life_change(
292  Thread_Control    *the_thread,
293  Thread_Control    *executing,
294  Priority_Control   priority,
295  Thread_Life_state  additional_life_state
296)
297{
298  Thread_Life_state previous_life_state;
299  Per_CPU_Control *cpu;
300  ISR_Level level;
301  const Scheduler_Control *scheduler;
302
303  cpu = _Thread_Action_ISR_disable_and_acquire( the_thread, &level );
304  previous_life_state = the_thread->Life.state;
305  the_thread->Life.state = previous_life_state | additional_life_state;
306  _Thread_Action_release_and_ISR_enable( cpu, level );
307
308  scheduler = _Scheduler_Get( the_thread );
309  if ( the_thread == executing ) {
310    Priority_Control unused;
311
312    _Thread_Set_priority( the_thread, priority, &unused, true );
313    _Thread_Start_life_change_for_executing( executing );
314  } else if ( previous_life_state == THREAD_LIFE_NORMAL ) {
315    _Thread_Start_life_change( the_thread, scheduler, priority );
316  } else {
317    _Thread_Clear_state( the_thread, STATES_SUSPENDED );
318
319    if ( _Thread_Is_life_terminating( additional_life_state ) ) {
320      _Thread_Change_priority(
321        the_thread,
322        priority,
323        NULL,
324        _Thread_Raise_real_priority_filter,
325        false
326      );
327    }
328  }
329}
330
331void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
332{
333  _Assert( _Thread_Is_life_protected( executing->Life.state ) );
334
335  if ( _States_Is_dormant( the_thread->current_state ) ) {
336    _Thread_Make_zombie( the_thread );
337  } else {
338    if (
339      the_thread != executing
340        && !_Thread_Is_life_terminating( executing->Life.state )
341    ) {
342      /*
343       * Wait for termination of victim thread.  If the executing thread is
344       * also terminated, then do not wait.  This avoids potential cyclic
345       * dependencies and thus dead lock.
346       */
347       the_thread->Life.terminator = executing;
348       _Thread_Set_state( executing, STATES_WAITING_FOR_TERMINATION );
349    }
350
351    _Thread_Request_life_change(
352      the_thread,
353      executing,
354      executing->current_priority,
355      THREAD_LIFE_TERMINATING
356    );
357  }
358}
359
360bool _Thread_Restart(
361  Thread_Control                 *the_thread,
362  Thread_Control                 *executing,
363  const Thread_Entry_information *entry
364)
365{
366  if ( !_States_Is_dormant( the_thread->current_state ) ) {
367    the_thread->Start.Entry = *entry;
368
369    _Thread_Request_life_change(
370      the_thread,
371      executing,
372      the_thread->Start.initial_priority,
373      THREAD_LIFE_RESTARTING
374    );
375
376    return true;
377  }
378
379  return false;
380}
381
382bool _Thread_Set_life_protection( bool protect )
383{
384  bool previous_life_protection;
385  ISR_Level level;
386  Per_CPU_Control *cpu;
387  Thread_Control *executing;
388  Thread_Life_state previous_life_state;
389
390  cpu = _Thread_Action_ISR_disable_and_acquire_for_executing( &level );
391  executing = cpu->executing;
392
393  previous_life_state = executing->Life.state;
394  previous_life_protection = _Thread_Is_life_protected( previous_life_state );
395
396  if ( protect ) {
397    executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
398  } else {
399    executing->Life.state = previous_life_state & ~THREAD_LIFE_PROTECTED;
400  }
401
402  _Thread_Action_release_and_ISR_enable( cpu, level );
403
404#if defined(RTEMS_SMP)
405  /*
406   * On SMP configurations it is possible that a life change of an executing
407   * thread is requested, but this thread didn't notice it yet.  The life
408   * change is first marked in the life state field and then all scheduling and
409   * other thread state updates are performed.  The last step is to issues an
410   * inter-processor interrupt if necessary.  Since this takes some time we
411   * have to synchronize here.
412   */
413  if (
414    !_Thread_Is_life_protected( previous_life_state )
415      && _Thread_Is_life_changing( previous_life_state )
416  ) {
417    _Thread_Disable_dispatch();
418    _Thread_Enable_dispatch();
419  }
420#endif
421
422  if (
423    !protect
424      && _Thread_Is_life_changing( previous_life_state )
425  ) {
426    _Thread_Disable_dispatch();
427    _Thread_Start_life_change_for_executing( executing );
428    _Thread_Enable_dispatch();
429  }
430
431  return previous_life_protection;
432}
Note: See TracBrowser for help on using the repository browser.