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

5
Last change on this file since d7665823 was d7665823, checked in by Sebastian Huber <sebastian.huber@…>, on 06/24/15 at 13:43:19

score: Introduce Thread_queue_Heads

Move the storage for the thread queue heads to the threads. Each thread
provides a set of thread queue heads allocated from a dedicated memory
pool. In case a thread blocks on a queue, then it lends its heads to
the queue. In case the thread unblocks, then it takes a free set of
threads from the queue. Since a thread can block on at most one queue
this works. This mechanism is used in FreeBSD. The motivation for this
change is to reduce the memory demands of the synchronization objects.
On a 32-bit uni-processor configuration the Thread_queue_Control size is
now 8 bytes, compared to 64 bytes in RTEMS 4.10 (other changes reduced
the size as well).

  • Property mode set to 100644
File size: 11.3 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  _ISR_lock_Destroy( &the_thread->Lock.Default );
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_Start_life_change_for_executing(
178  Thread_Control *executing
179)
180{
181  _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
182  _Assert(
183    executing->current_state == STATES_READY
184      || executing->current_state == STATES_SUSPENDED
185  );
186
187  _Thread_Add_post_switch_action( executing, &executing->Life.Action );
188}
189
190void _Thread_Life_action_handler(
191  Thread_Control  *executing,
192  Thread_Action   *action,
193  Per_CPU_Control *cpu,
194  ISR_Level        level
195)
196{
197  Thread_Life_state previous_life_state;
198
199  (void) action;
200
201  previous_life_state = executing->Life.state;
202  executing->Life.state = THREAD_LIFE_PROTECTED;
203
204  _Thread_Action_release_and_ISR_enable( cpu, level );
205
206  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
207    _User_extensions_Thread_terminate( executing );
208  } else {
209    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
210
211    _User_extensions_Thread_restart( executing );
212  }
213
214  _Thread_Disable_dispatch();
215
216  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
217    _Thread_Make_zombie( executing );
218
219    if ( executing->Life.terminator != NULL ) {
220      _Thread_Clear_state(
221        executing->Life.terminator,
222        STATES_WAITING_FOR_TERMINATION
223      );
224    }
225
226    _Thread_Enable_dispatch();
227
228    _Assert_Not_reached();
229  } else {
230    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
231
232    if ( _Thread_Is_life_terminating( executing->Life.state ) ) {
233      /* Someone deleted us in the mean-time */
234      _Thread_Start_life_change_for_executing( executing );
235    } else {
236      _Assert( executing->Timer.state == WATCHDOG_INACTIVE );
237      _Assert(
238        executing->current_state == STATES_READY
239          || executing->current_state == STATES_SUSPENDED
240      );
241
242      executing->Life.state = THREAD_LIFE_NORMAL;
243
244      _Thread_Load_environment( executing );
245      _Thread_Restart_self( executing );
246
247      _Assert_Not_reached();
248    }
249  }
250}
251
252static void _Thread_Start_life_change(
253  Thread_Control          *the_thread,
254  const Scheduler_Control *scheduler,
255  Priority_Control         priority
256)
257{
258  the_thread->is_preemptible   = the_thread->Start.is_preemptible;
259  the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
260  the_thread->budget_callout   = the_thread->Start.budget_callout;
261
262  _Thread_Set_state( the_thread, STATES_RESTARTING );
263  _Thread_queue_Extract_with_proxy( the_thread );
264  _Watchdog_Remove_ticks( &the_thread->Timer );
265  _Thread_Change_priority(
266    the_thread,
267    priority,
268    NULL,
269    _Thread_Raise_real_priority_filter,
270    false
271  );
272  _Thread_Add_post_switch_action( the_thread, &the_thread->Life.Action );
273  _Thread_Ready( the_thread );
274}
275
276static void _Thread_Request_life_change(
277  Thread_Control    *the_thread,
278  Thread_Control    *executing,
279  Priority_Control   priority,
280  Thread_Life_state  additional_life_state
281)
282{
283  Thread_Life_state previous_life_state;
284  Per_CPU_Control *cpu;
285  ISR_Level level;
286  const Scheduler_Control *scheduler;
287
288  cpu = _Thread_Action_ISR_disable_and_acquire( the_thread, &level );
289  previous_life_state = the_thread->Life.state;
290  the_thread->Life.state = previous_life_state | additional_life_state;
291  _Thread_Action_release_and_ISR_enable( cpu, level );
292
293  scheduler = _Scheduler_Get( the_thread );
294  if ( the_thread == executing ) {
295    Priority_Control unused;
296
297    _Thread_Set_priority( the_thread, priority, &unused, true );
298    _Thread_Start_life_change_for_executing( executing );
299  } else if ( previous_life_state == THREAD_LIFE_NORMAL ) {
300    _Thread_Start_life_change( the_thread, scheduler, priority );
301  } else {
302    _Thread_Clear_state( the_thread, STATES_SUSPENDED );
303
304    if ( _Thread_Is_life_terminating( additional_life_state ) ) {
305      _Thread_Change_priority(
306        the_thread,
307        priority,
308        NULL,
309        _Thread_Raise_real_priority_filter,
310        false
311      );
312    }
313  }
314}
315
316void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
317{
318  _Assert( _Thread_Is_life_protected( executing->Life.state ) );
319
320  if ( _States_Is_dormant( the_thread->current_state ) ) {
321    _Thread_Make_zombie( the_thread );
322  } else {
323    if (
324      the_thread != executing
325        && !_Thread_Is_life_terminating( executing->Life.state )
326    ) {
327      /*
328       * Wait for termination of victim thread.  If the executing thread is
329       * also terminated, then do not wait.  This avoids potential cyclic
330       * dependencies and thus dead lock.
331       */
332       the_thread->Life.terminator = executing;
333       _Thread_Set_state( executing, STATES_WAITING_FOR_TERMINATION );
334    }
335
336    _Thread_Request_life_change(
337      the_thread,
338      executing,
339      executing->current_priority,
340      THREAD_LIFE_TERMINATING
341    );
342  }
343}
344
345bool _Thread_Restart(
346  Thread_Control            *the_thread,
347  Thread_Control            *executing,
348  void                      *pointer_argument,
349  Thread_Entry_numeric_type  numeric_argument
350)
351{
352  if ( !_States_Is_dormant( the_thread->current_state ) ) {
353    the_thread->Start.pointer_argument = pointer_argument;
354    the_thread->Start.numeric_argument = numeric_argument;
355
356    _Thread_Request_life_change(
357      the_thread,
358      executing,
359      the_thread->Start.initial_priority,
360      THREAD_LIFE_RESTARTING
361    );
362
363    return true;
364  }
365
366  return false;
367}
368
369bool _Thread_Set_life_protection( bool protect )
370{
371  bool previous_life_protection;
372  ISR_Level level;
373  Per_CPU_Control *cpu;
374  Thread_Control *executing;
375  Thread_Life_state previous_life_state;
376
377  cpu = _Thread_Action_ISR_disable_and_acquire_for_executing( &level );
378  executing = cpu->executing;
379
380  previous_life_state = executing->Life.state;
381  previous_life_protection = _Thread_Is_life_protected( previous_life_state );
382
383  if ( protect ) {
384    executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
385  } else {
386    executing->Life.state = previous_life_state & ~THREAD_LIFE_PROTECTED;
387  }
388
389  _Thread_Action_release_and_ISR_enable( cpu, level );
390
391#if defined(RTEMS_SMP)
392  /*
393   * On SMP configurations it is possible that a life change of an executing
394   * thread is requested, but this thread didn't notice it yet.  The life
395   * change is first marked in the life state field and then all scheduling and
396   * other thread state updates are performed.  The last step is to issues an
397   * inter-processor interrupt if necessary.  Since this takes some time we
398   * have to synchronize here.
399   */
400  if (
401    !_Thread_Is_life_protected( previous_life_state )
402      && _Thread_Is_life_changing( previous_life_state )
403  ) {
404    _Thread_Disable_dispatch();
405    _Thread_Enable_dispatch();
406  }
407#endif
408
409  if (
410    !protect
411      && _Thread_Is_life_changing( previous_life_state )
412  ) {
413    _Thread_Disable_dispatch();
414    _Thread_Start_life_change_for_executing( executing );
415    _Thread_Enable_dispatch();
416  }
417
418  return previous_life_protection;
419}
Note: See TracBrowser for help on using the repository browser.