source: rtems/cpukit/score/src/threadrestart.c @ 0e3c59d6

5
Last change on this file since 0e3c59d6 was 0e3c59d6, checked in by Sebastian Huber <sebastian.huber@…>, on 06/26/15 at 10:54:33

score: Use a plain ticket lock for thread locks

This enables external libraries to use thread locks since they are
independent of the actual RTEMS build configuration, e.g. profiling
enabled or disabled.

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