source: rtems/cpukit/score/src/threadrestart.c @ 69dd99b

5
Last change on this file since 69dd99b was 69dd99b, checked in by Sebastian Huber <sebastian.huber@…>, on 08/05/16 at 06:19:39

score: Simplify _Thread_Finalize_life_change()

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