source: rtems/cpukit/score/src/threadrestart.c @ 21275b58

Last change on this file since 21275b58 was 21275b58, checked in by Sebastian Huber <sebastian.huber@…>, on Nov 22, 2018 at 6:14:51 PM

score: Static Objects_Information initialization

Statically allocate the objects information together with the initial
set of objects either via <rtems/confdefs.h>. Provide default object
informations with zero objects via librtemscpu.a. This greatly
simplifies the workspace size estimate. RTEMS applications which do not
use the unlimited objects option are easier to debug since all objects
reside now in statically allocated objects of the right types.

Close #3621.

  • Property mode set to 100644
File size: 17.6 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, 2016 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
35#define THREAD_JOIN_TQ_OPERATIONS &_Thread_queue_Operations_priority
36
37static void _Thread_Life_action_handler(
38  Thread_Control   *executing,
39  Thread_Action    *action,
40  ISR_lock_Context *lock_context
41);
42
43typedef struct {
44  Chain_Control Chain;
45  ISR_lock_Control Lock;
46} Thread_Zombie_control;
47
48static Thread_Zombie_control _Thread_Zombies = {
49  .Chain = CHAIN_INITIALIZER_EMPTY( _Thread_Zombies.Chain ),
50  .Lock = ISR_LOCK_INITIALIZER( "thread zombies" )
51};
52
53static void _Thread_Raise_real_priority(
54  Thread_Control   *the_thread,
55  Priority_Control  priority
56)
57{
58  Thread_queue_Context queue_context;
59
60  _Thread_queue_Context_initialize( &queue_context );
61  _Thread_queue_Context_clear_priority_updates( &queue_context );
62  _Thread_Wait_acquire( the_thread, &queue_context );
63
64  if ( priority < the_thread->Real_priority.priority ) {
65    _Thread_Priority_change(
66      the_thread,
67      &the_thread->Real_priority,
68      priority,
69      false,
70      &queue_context
71    );
72  }
73
74  _Thread_Wait_release( the_thread, &queue_context );
75  _Thread_Priority_update( &queue_context );
76}
77
78typedef struct {
79  Thread_queue_Context  Base;
80  void                 *exit_value;
81} Thread_Join_context;
82
83static Thread_Control *_Thread_Join_flush_filter(
84  Thread_Control       *the_thread,
85  Thread_queue_Queue   *queue,
86  Thread_queue_Context *queue_context
87)
88{
89  Thread_Join_context *join_context;
90
91  join_context = (Thread_Join_context *) queue_context;
92
93  the_thread->Wait.return_argument = join_context->exit_value;
94
95  return the_thread;
96}
97
98static void _Thread_Wake_up_joining_threads( Thread_Control *the_thread )
99{
100  Thread_Join_context join_context;
101
102  join_context.exit_value = the_thread->Life.exit_value;
103
104  _Thread_queue_Context_initialize( &join_context.Base );
105  _Thread_queue_Acquire( &the_thread->Join_queue, &join_context.Base );
106  _Thread_queue_Flush_critical(
107    &the_thread->Join_queue.Queue,
108    THREAD_JOIN_TQ_OPERATIONS,
109    _Thread_Join_flush_filter,
110    &join_context.Base
111  );
112}
113
114static void _Thread_Add_to_zombie_chain( Thread_Control *the_thread )
115{
116  ISR_lock_Context       lock_context;
117  Thread_Zombie_control *zombies;
118
119  zombies = &_Thread_Zombies;
120  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
121  _Chain_Append_unprotected( &zombies->Chain, &the_thread->Object.Node );
122  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
123}
124
125static void _Thread_Make_zombie( Thread_Control *the_thread )
126{
127#if defined(RTEMS_SCORE_THREAD_ENABLE_RESOURCE_COUNT)
128  if ( _Thread_Owns_resources( the_thread ) ) {
129    _Internal_error( INTERNAL_ERROR_RESOURCE_IN_USE );
130  }
131#endif
132
133  _Objects_Close(
134    _Objects_Get_information_id( the_thread->Object.id ),
135    &the_thread->Object
136  );
137
138  _Thread_Set_state( the_thread, STATES_ZOMBIE );
139  _Thread_queue_Extract_with_proxy( the_thread );
140  _Thread_Timer_remove( the_thread );
141
142  /*
143   * Add the thread to the thread zombie chain before we wake up joining
144   * threads, so that they are able to clean up the thread immediately.  This
145   * matters for SMP configurations.
146   */
147  _Thread_Add_to_zombie_chain( the_thread );
148
149  _Thread_Wake_up_joining_threads( the_thread );
150}
151
152static void _Thread_Free( Thread_Control *the_thread )
153{
154  Thread_Information *information = (Thread_Information *)
155    _Objects_Get_information_id( the_thread->Object.id );
156
157  _User_extensions_Thread_delete( the_thread );
158  _User_extensions_Destroy_iterators( the_thread );
159  _ISR_lock_Destroy( &the_thread->Keys.Lock );
160  _Scheduler_Node_destroy(
161    _Thread_Scheduler_get_home( the_thread ),
162    _Thread_Scheduler_get_home_node( the_thread )
163  );
164  _ISR_lock_Destroy( &the_thread->Timer.Lock );
165
166  /*
167   *  The thread might have been FP.  So deal with that.
168   */
169#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
170#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE )
171  if ( _Thread_Is_allocated_fp( the_thread ) )
172    _Thread_Deallocate_fp();
173#endif
174
175  _Workspace_Free( the_thread->Start.fp_context );
176#endif
177
178  _Freechain_Put(
179    &information->Thread_queue_heads.Free,
180    the_thread->Wait.spare_heads
181  );
182
183  /*
184   *  Free the rest of the memory associated with this task
185   *  and set the associated pointers to NULL for safety.
186   */
187  _Thread_Stack_Free( the_thread );
188
189  _Workspace_Free( the_thread->Start.tls_area );
190
191#if defined(RTEMS_SMP)
192  _ISR_lock_Destroy( &the_thread->Scheduler.Lock );
193  _ISR_lock_Destroy( &the_thread->Wait.Lock.Default );
194  _SMP_lock_Stats_destroy( &the_thread->Potpourri_stats );
195#endif
196
197  _Thread_queue_Destroy( &the_thread->Join_queue );
198  _Context_Destroy( the_thread, &the_thread->Registers );
199  _Objects_Free( &information->Objects, &the_thread->Object );
200}
201
202static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
203{
204#if defined(RTEMS_SMP)
205  /*
206   * It is very unlikely that we see an executing thread here.  It can happen
207   * in case the thread termination sequence is interrupted by a slow interrupt
208   * service on a remote processor.
209   */
210  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
211    /* Wait */
212  }
213#else
214  (void) the_thread;
215#endif
216}
217
218void _Thread_Kill_zombies( void )
219{
220  ISR_lock_Context lock_context;
221  Thread_Zombie_control *zombies = &_Thread_Zombies;
222  Thread_Control *the_thread;
223
224  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
225
226  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
227  while ( the_thread != NULL ) {
228    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
229
230    _Thread_Wait_for_execution_stop( the_thread );
231    _Thread_Free( the_thread );
232
233    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
234
235    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
236  }
237
238  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
239}
240
241static Thread_Life_state _Thread_Change_life_locked(
242  Thread_Control    *the_thread,
243  Thread_Life_state  clear,
244  Thread_Life_state  set,
245  Thread_Life_state  ignore
246)
247{
248  Thread_Life_state previous;
249  Thread_Life_state state;
250
251  previous = the_thread->Life.state;
252  state = previous;
253  state &= ~clear;
254  state |= set;
255  the_thread->Life.state = state;
256
257  state &= ~ignore;
258
259  if (
260    _Thread_Is_life_change_allowed( state )
261      && _Thread_Is_life_changing( state )
262  ) {
263    the_thread->is_preemptible   = the_thread->Start.is_preemptible;
264    the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
265    the_thread->budget_callout   = the_thread->Start.budget_callout;
266
267    _Thread_Add_post_switch_action(
268      the_thread,
269      &the_thread->Life.Action,
270      _Thread_Life_action_handler
271    );
272  }
273
274  return previous;
275}
276
277static Per_CPU_Control *_Thread_Wait_for_join(
278  Thread_Control  *executing,
279  Per_CPU_Control *cpu_self
280)
281{
282  ISR_lock_Context lock_context;
283
284  _Thread_State_acquire( executing, &lock_context );
285
286  if (
287    _Thread_Is_joinable( executing )
288      && _Thread_queue_Is_empty( &executing->Join_queue.Queue )
289  ) {
290    _Thread_Set_state_locked( executing, STATES_WAITING_FOR_JOIN_AT_EXIT );
291    _Thread_State_release( executing, &lock_context );
292    _Thread_Dispatch_direct( cpu_self );
293
294    /* Let other threads run */
295
296    cpu_self = _Thread_Dispatch_disable();
297  } else {
298    _Thread_State_release( executing, &lock_context );
299  }
300
301  return cpu_self;
302}
303
304void _Thread_Life_action_handler(
305  Thread_Control   *executing,
306  Thread_Action    *action,
307  ISR_lock_Context *lock_context
308)
309{
310  Thread_Life_state  previous_life_state;
311  Per_CPU_Control   *cpu_self;
312
313  (void) action;
314
315  previous_life_state = executing->Life.state;
316  executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
317
318  _Thread_State_release( executing, lock_context );
319
320  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
321    _User_extensions_Thread_terminate( executing );
322  } else {
323    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
324
325    _User_extensions_Thread_restart( executing );
326  }
327
328  cpu_self = _Thread_Dispatch_disable();
329
330  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
331    cpu_self = _Thread_Wait_for_join( executing, cpu_self );
332    _Thread_Make_zombie( executing );
333    _Thread_Dispatch_direct( cpu_self );
334    RTEMS_UNREACHABLE();
335  }
336
337  _Assert( _Thread_Is_life_restarting( previous_life_state ) );
338
339  _Thread_State_acquire( executing, lock_context );
340
341  _Thread_Change_life_locked(
342    executing,
343    THREAD_LIFE_PROTECTED | THREAD_LIFE_RESTARTING,
344    0,
345    0
346  );
347
348  _Thread_State_release( executing, lock_context );
349
350  _Assert(
351    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
352  );
353  _Assert(
354    executing->current_state == STATES_READY
355      || executing->current_state == STATES_SUSPENDED
356  );
357
358  _User_extensions_Destroy_iterators( executing );
359  _Thread_Load_environment( executing );
360
361#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
362  if ( executing->fp_context != NULL ) {
363    _Context_Restore_fp( &executing->fp_context );
364  }
365#endif
366
367  _Context_Restart_self( &executing->Registers );
368  RTEMS_UNREACHABLE();
369}
370
371static void _Thread_Add_life_change_request( Thread_Control *the_thread )
372{
373  uint32_t pending_requests;
374
375  _Assert( _Thread_State_is_owner( the_thread ) );
376
377  pending_requests = the_thread->Life.pending_life_change_requests;
378  the_thread->Life.pending_life_change_requests = pending_requests + 1;
379
380  if ( pending_requests == 0 ) {
381    _Thread_Set_state_locked( the_thread, STATES_LIFE_IS_CHANGING );
382  }
383}
384
385static void _Thread_Remove_life_change_request( Thread_Control *the_thread )
386{
387  ISR_lock_Context lock_context;
388  uint32_t         pending_requests;
389
390  _Thread_State_acquire( the_thread, &lock_context );
391
392  pending_requests = the_thread->Life.pending_life_change_requests;
393  the_thread->Life.pending_life_change_requests = pending_requests - 1;
394
395  if ( pending_requests == 1 ) {
396    /*
397     * Do not remove states used for thread queues to avoid race conditions on
398     * SMP configurations.  We could interrupt an extract operation on another
399     * processor disregarding the thread wait flags.  Rely on
400     * _Thread_queue_Extract_with_proxy() for removal of these states.
401     */
402    _Thread_Clear_state_locked(
403      the_thread,
404      STATES_LIFE_IS_CHANGING | STATES_SUSPENDED
405        | ( STATES_BLOCKED & ~STATES_LOCALLY_BLOCKED )
406    );
407  }
408
409  _Thread_State_release( the_thread, &lock_context );
410}
411
412static void _Thread_Finalize_life_change(
413  Thread_Control   *the_thread,
414  Priority_Control  priority
415)
416{
417  _Thread_queue_Extract_with_proxy( the_thread );
418  _Thread_Timer_remove( the_thread );
419  _Thread_Raise_real_priority( the_thread, priority );
420  _Thread_Remove_life_change_request( the_thread );
421}
422
423void _Thread_Join(
424  Thread_Control       *the_thread,
425  States_Control        waiting_for_join,
426  Thread_Control       *executing,
427  Thread_queue_Context *queue_context
428)
429{
430  _Assert( the_thread != executing );
431  _Assert( _Thread_State_is_owner( the_thread ) );
432
433  executing->Wait.return_argument = NULL;
434
435  _Thread_queue_Context_set_thread_state( queue_context, waiting_for_join );
436  _Thread_queue_Enqueue(
437    &the_thread->Join_queue.Queue,
438    THREAD_JOIN_TQ_OPERATIONS,
439    executing,
440    queue_context
441  );
442}
443
444static void _Thread_Set_exit_value(
445  Thread_Control *the_thread,
446  void           *exit_value
447)
448{
449  the_thread->Life.exit_value = exit_value;
450}
451
452void _Thread_Cancel(
453  Thread_Control *the_thread,
454  Thread_Control *executing,
455  void           *exit_value
456)
457{
458  ISR_lock_Context   lock_context;
459  Thread_Life_state  previous;
460  Per_CPU_Control   *cpu_self;
461  Priority_Control   priority;
462
463  _Assert( the_thread != executing );
464
465  _Thread_State_acquire( the_thread, &lock_context );
466
467  _Thread_Set_exit_value( the_thread, exit_value );
468  previous = _Thread_Change_life_locked(
469    the_thread,
470    0,
471    THREAD_LIFE_TERMINATING,
472    0
473  );
474
475  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
476  priority = _Thread_Get_priority( executing );
477
478  if ( _States_Is_dormant( the_thread->current_state ) ) {
479    _Thread_State_release( the_thread, &lock_context );
480    _Thread_Make_zombie( the_thread );
481  } else if ( _Thread_Is_life_change_allowed( previous ) ) {
482    _Thread_Add_life_change_request( the_thread );
483    _Thread_State_release( the_thread, &lock_context );
484
485    _Thread_Finalize_life_change( the_thread, priority );
486  } else {
487    _Thread_Add_life_change_request( the_thread );
488    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
489    _Thread_State_release( the_thread, &lock_context );
490
491    _Thread_Raise_real_priority( the_thread, priority );
492    _Thread_Remove_life_change_request( the_thread );
493  }
494
495  _Thread_Dispatch_enable( cpu_self );
496}
497
498static void _Thread_Close_enqueue_callout(
499  Thread_queue_Queue   *queue,
500  Thread_Control       *the_thread,
501  Per_CPU_Control      *cpu_self,
502  Thread_queue_Context *queue_context
503)
504{
505  Thread_Close_context *context;
506
507  context = (Thread_Close_context *) queue_context;
508  _Thread_Cancel( context->cancel, the_thread, NULL );
509}
510
511void _Thread_Close(
512  Thread_Control       *the_thread,
513  Thread_Control       *executing,
514  Thread_Close_context *context
515)
516{
517  context->cancel = the_thread;
518  _Thread_queue_Context_set_enqueue_callout(
519    &context->Base,
520    _Thread_Close_enqueue_callout
521  );
522  _Thread_State_acquire_critical(
523    the_thread,
524    &context->Base.Lock_context.Lock_context
525  );
526  _Thread_Join(
527    the_thread,
528    STATES_WAITING_FOR_JOIN,
529    executing,
530    &context->Base
531  );
532}
533
534void _Thread_Exit(
535  Thread_Control    *executing,
536  Thread_Life_state  set,
537  void              *exit_value
538)
539{
540  ISR_lock_Context lock_context;
541
542  _Assert(
543    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
544  );
545  _Assert(
546    executing->current_state == STATES_READY
547      || executing->current_state == STATES_SUSPENDED
548  );
549
550  _Thread_State_acquire( executing, &lock_context );
551  _Thread_Set_exit_value( executing, exit_value );
552  _Thread_Change_life_locked(
553    executing,
554    0,
555    set,
556    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
557  );
558  _Thread_State_release( executing, &lock_context );
559}
560
561bool _Thread_Restart_other(
562  Thread_Control                 *the_thread,
563  const Thread_Entry_information *entry,
564  ISR_lock_Context               *lock_context
565)
566{
567  Thread_Life_state  previous;
568  Per_CPU_Control   *cpu_self;
569
570  _Thread_State_acquire_critical( the_thread, lock_context );
571
572  if ( _States_Is_dormant( the_thread->current_state ) ) {
573    _Thread_State_release( the_thread, lock_context );
574    return false;
575  }
576
577  the_thread->Start.Entry = *entry;
578  previous = _Thread_Change_life_locked(
579    the_thread,
580    0,
581    THREAD_LIFE_RESTARTING,
582    0
583  );
584
585  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
586
587  if ( _Thread_Is_life_change_allowed( previous ) ) {
588    _Thread_Add_life_change_request( the_thread );
589    _Thread_State_release( the_thread, lock_context );
590
591    _Thread_Finalize_life_change(
592      the_thread,
593      the_thread->Start.initial_priority
594    );
595  } else {
596    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
597    _Thread_State_release( the_thread, lock_context );
598  }
599
600  _Thread_Dispatch_enable( cpu_self );
601  return true;
602}
603
604void _Thread_Restart_self(
605  Thread_Control                 *executing,
606  const Thread_Entry_information *entry,
607  ISR_lock_Context               *lock_context
608)
609{
610  Per_CPU_Control      *cpu_self;
611  Thread_queue_Context  queue_context;
612
613  _Assert(
614    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
615  );
616  _Assert(
617    executing->current_state == STATES_READY
618      || executing->current_state == STATES_SUSPENDED
619  );
620
621  _Thread_queue_Context_initialize( &queue_context );
622  _Thread_queue_Context_clear_priority_updates( &queue_context );
623  _Thread_State_acquire_critical( executing, lock_context );
624
625  executing->Start.Entry = *entry;
626  _Thread_Change_life_locked(
627    executing,
628    0,
629    THREAD_LIFE_RESTARTING,
630    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
631  );
632
633  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
634  _Thread_State_release( executing, lock_context );
635
636  _Thread_Wait_acquire_default( executing, lock_context );
637  _Thread_Priority_change(
638    executing,
639    &executing->Real_priority,
640    executing->Start.initial_priority,
641    false,
642    &queue_context
643  );
644  _Thread_Wait_release_default( executing, lock_context );
645
646  _Thread_Priority_update( &queue_context );
647  _Thread_Dispatch_direct( cpu_self );
648  RTEMS_UNREACHABLE();
649}
650
651Thread_Life_state _Thread_Change_life(
652  Thread_Life_state clear,
653  Thread_Life_state set,
654  Thread_Life_state ignore
655)
656{
657  ISR_lock_Context   lock_context;
658  Thread_Control    *executing;
659  Per_CPU_Control   *cpu_self;
660  Thread_Life_state  previous;
661
662  executing = _Thread_State_acquire_for_executing( &lock_context );
663
664  previous = _Thread_Change_life_locked( executing, clear, set, ignore );
665
666  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
667  _Thread_State_release( executing, &lock_context );
668  _Thread_Dispatch_enable( cpu_self );
669
670  return previous;
671}
672
673Thread_Life_state _Thread_Set_life_protection( Thread_Life_state state )
674{
675  return _Thread_Change_life(
676    THREAD_LIFE_PROTECTED,
677    state & THREAD_LIFE_PROTECTED,
678    0
679  );
680}
Note: See TracBrowser for help on using the repository browser.