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

Last change on this file was 4c20da4, checked in by Sebastian Huber <sebastian.huber@…>, on Apr 4, 2019 at 7:18:11 AM

doxygen: Rename Score* groups in RTEMSScore*

Update #3706

  • Property mode set to 100644
File size: 17.6 KB
Line 
1/**
2 * @file
3 *
4 * @brief Restart Thread
5 * @ingroup RTEMSScoreThread
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.