source: rtems/cpukit/score/src/threadrestart.c @ 900d337f

4.115
Last change on this file since 900d337f was 900d337f, checked in by Sebastian Huber <sebastian.huber@…>, on 05/05/15 at 11:05:54

score: Rework _Thread_Change_priority()

Move the writes to Thread_Control::current_priority and
Thread_Control::real_priority into _Thread_Change_priority() under the
protection of the thread lock. Add a filter function to
_Thread_Change_priority() to enable specialized variants.

Avoid race conditions during a thread priority restore with the new
Thread_Control::priority_restore_hint for an important average case
optimizations used by priority inheritance mutexes.

Update #2273.

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