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

5
Last change on this file since 0daa8ab was b2e1bded, checked in by Sebastian Huber <sebastian.huber@…>, on 07/18/17 at 12:53:16

score: Add optional _CPU_Context_Destroy()

Update #3077.

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