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

5
Last change on this file since df2177ab was df2177ab, checked in by Sebastian Huber <sebastian.huber@…>, on 07/01/16 at 12:47:07

score: Change scheduler node init and destroy

Provide the scheduler node to initialize or destroy to the corresponding
operations. This makes it possible to have more than one scheduler node
per thread.

  • 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 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  _SMP_ticket_lock_Destroy( &the_thread->Lock.Default );
219  _SMP_lock_Stats_destroy( &the_thread->Lock.Stats );
220  _SMP_lock_Stats_destroy( &the_thread->Potpourri_stats );
221#endif
222
223  _Thread_queue_Destroy( &the_thread->Join_queue );
224
225  _Objects_Free( &information->Objects, &the_thread->Object );
226}
227
228static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
229{
230#if defined(RTEMS_SMP)
231  /*
232   * It is very unlikely that we see an executing thread here.  It can happen
233   * in case the thread termination sequence is interrupted by a slow interrupt
234   * service on a remote processor.
235   */
236  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
237    /* Wait */
238  }
239#else
240  (void) the_thread;
241#endif
242}
243
244void _Thread_Kill_zombies( void )
245{
246  ISR_lock_Context lock_context;
247  Thread_Zombie_control *zombies = &_Thread_Zombies;
248  Thread_Control *the_thread;
249
250  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
251
252  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
253  while ( the_thread != NULL ) {
254    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
255
256    _Thread_Wait_for_execution_stop( the_thread );
257    _Thread_Free( the_thread );
258
259    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
260
261    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
262  }
263
264  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
265}
266
267static Thread_Life_state _Thread_Change_life_locked(
268  Thread_Control    *the_thread,
269  Thread_Life_state  clear,
270  Thread_Life_state  set,
271  Thread_Life_state  ignore
272)
273{
274  Thread_Life_state previous;
275  Thread_Life_state state;
276
277  previous = the_thread->Life.state;
278  state = previous;
279  state &= ~clear;
280  state |= set;
281  the_thread->Life.state = state;
282
283  state &= ~ignore;
284
285  if (
286    _Thread_Is_life_change_allowed( state )
287      && _Thread_Is_life_changing( state )
288  ) {
289    the_thread->is_preemptible   = the_thread->Start.is_preemptible;
290    the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
291    the_thread->budget_callout   = the_thread->Start.budget_callout;
292
293    _Thread_Add_post_switch_action(
294      the_thread,
295      &the_thread->Life.Action,
296      _Thread_Life_action_handler
297    );
298  }
299
300  return previous;
301}
302
303static Per_CPU_Control *_Thread_Wait_for_join(
304  Thread_Control  *executing,
305  Per_CPU_Control *cpu_self
306)
307{
308#if defined(RTEMS_POSIX_API)
309  ISR_lock_Context lock_context;
310
311  _Thread_State_acquire( executing, &lock_context );
312
313  if (
314    _Thread_Is_joinable( executing )
315      && _Thread_queue_Is_empty( &executing->Join_queue.Queue )
316  ) {
317    _Thread_Set_state_locked( executing, STATES_WAITING_FOR_JOIN_AT_EXIT );
318    _Thread_State_release( executing, &lock_context );
319    _Thread_Dispatch_enable( cpu_self );
320
321    /* Let other threads run */
322
323    cpu_self = _Thread_Dispatch_disable();
324  } else {
325    _Thread_State_release( executing, &lock_context );
326  }
327#endif
328
329  return cpu_self;
330}
331
332void _Thread_Life_action_handler(
333  Thread_Control   *executing,
334  Thread_Action    *action,
335  ISR_lock_Context *lock_context
336)
337{
338  Thread_Life_state  previous_life_state;
339  Per_CPU_Control   *cpu_self;
340
341  (void) action;
342
343  previous_life_state = executing->Life.state;
344  executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
345
346  _Thread_State_release( executing, lock_context );
347
348  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
349    _User_extensions_Thread_terminate( executing );
350  } else {
351    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
352
353    _User_extensions_Thread_restart( executing );
354  }
355
356  cpu_self = _Thread_Dispatch_disable();
357
358  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
359    cpu_self = _Thread_Wait_for_join( executing, cpu_self );
360
361    _Thread_Make_zombie( executing );
362
363    /* FIXME: Workaround for https://devel.rtems.org/ticket/2751 */
364    cpu_self->dispatch_necessary = true;
365
366    _Assert( cpu_self->heir != executing );
367    _Thread_Dispatch_enable( cpu_self );
368    RTEMS_UNREACHABLE();
369  }
370
371  _Assert( _Thread_Is_life_restarting( previous_life_state ) );
372
373  _Thread_State_acquire( executing, lock_context );
374
375  _Thread_Change_life_locked(
376    executing,
377    THREAD_LIFE_PROTECTED | THREAD_LIFE_RESTARTING,
378    0,
379    0
380  );
381
382  _Thread_State_release( executing, lock_context );
383
384  _Assert(
385    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
386  );
387  _Assert(
388    executing->current_state == STATES_READY
389      || executing->current_state == STATES_SUSPENDED
390  );
391
392  _User_extensions_Destroy_iterators( executing );
393  _Thread_Load_environment( executing );
394
395#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
396  if ( executing->fp_context != NULL ) {
397    _Context_Restore_fp( &executing->fp_context );
398  }
399#endif
400
401  _Context_Restart_self( &executing->Registers );
402  RTEMS_UNREACHABLE();
403}
404
405static void _Thread_Add_life_change_request( Thread_Control *the_thread )
406{
407  uint32_t pending_requests;
408
409  _Assert( _Thread_State_is_owner( the_thread ) );
410
411  pending_requests = the_thread->Life.pending_life_change_requests;
412  the_thread->Life.pending_life_change_requests = pending_requests + 1;
413
414  if ( pending_requests == 0 ) {
415    _Thread_Set_state_locked( the_thread, STATES_LIFE_IS_CHANGING );
416  }
417}
418
419static void _Thread_Remove_life_change_request( Thread_Control *the_thread )
420{
421  ISR_lock_Context lock_context;
422  uint32_t         pending_requests;
423
424  _Thread_State_acquire( the_thread, &lock_context );
425
426  pending_requests = the_thread->Life.pending_life_change_requests;
427  the_thread->Life.pending_life_change_requests = pending_requests - 1;
428
429  if ( pending_requests == 1 ) {
430    /*
431     * Do not remove states used for thread queues to avoid race conditions on
432     * SMP configurations.  We could interrupt an extract operation on another
433     * processor disregarding the thread wait flags.  Rely on
434     * _Thread_queue_Extract_with_proxy() for removal of these states.
435     */
436    _Thread_Clear_state_locked(
437      the_thread,
438      STATES_LIFE_IS_CHANGING | STATES_SUSPENDED
439        | ( STATES_BLOCKED & ~STATES_LOCALLY_BLOCKED )
440    );
441  }
442
443  _Thread_State_release( the_thread, &lock_context );
444}
445
446static void _Thread_Finalize_life_change(
447  Thread_Control   *the_thread,
448  Priority_Control  priority
449)
450{
451  _Thread_queue_Extract_with_proxy( the_thread );
452  _Thread_Timer_remove( the_thread );
453  _Thread_Change_priority(
454    the_thread,
455    priority,
456    NULL,
457    _Thread_Raise_real_priority_filter,
458    false
459  );
460  _Thread_Remove_life_change_request( the_thread );
461}
462
463void _Thread_Join(
464  Thread_Control       *the_thread,
465  States_Control        waiting_for_join,
466  Thread_Control       *executing,
467  Thread_queue_Context *queue_context
468)
469{
470  _Assert( the_thread != executing );
471  _Assert( _Thread_State_is_owner( the_thread ) );
472
473#if defined(RTEMS_POSIX_API)
474  executing->Wait.return_argument = NULL;
475#endif
476
477  _Thread_queue_Enqueue_critical(
478    &the_thread->Join_queue.Queue,
479    THREAD_JOIN_TQ_OPERATIONS,
480    executing,
481    waiting_for_join,
482    WATCHDOG_NO_TIMEOUT,
483    queue_context
484  );
485}
486
487static void _Thread_Set_exit_value(
488  Thread_Control *the_thread,
489  void           *exit_value
490)
491{
492#if defined(RTEMS_POSIX_API)
493  the_thread->Life.exit_value = exit_value;
494#endif
495}
496
497void _Thread_Cancel(
498  Thread_Control *the_thread,
499  Thread_Control *executing,
500  void           *exit_value
501)
502{
503  ISR_lock_Context   lock_context;
504  Thread_Life_state  previous;
505  Per_CPU_Control   *cpu_self;
506  Priority_Control   priority;
507
508  _Assert( the_thread != executing );
509
510  _Thread_State_acquire( the_thread, &lock_context );
511
512  _Thread_Set_exit_value( the_thread, exit_value );
513  previous = _Thread_Change_life_locked(
514    the_thread,
515    0,
516    THREAD_LIFE_TERMINATING,
517    0
518  );
519
520  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
521  priority = executing->current_priority;
522
523  if ( _States_Is_dormant( the_thread->current_state ) ) {
524    _Thread_State_release( the_thread, &lock_context );
525    _Thread_Make_zombie( the_thread );
526  } else if ( _Thread_Is_life_change_allowed( previous ) ) {
527    _Thread_Add_life_change_request( the_thread );
528    _Thread_State_release( the_thread, &lock_context );
529
530    _Thread_Finalize_life_change( the_thread, priority );
531  } else {
532    _Thread_Add_life_change_request( the_thread );
533    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
534    _Thread_State_release( the_thread, &lock_context );
535
536    _Thread_Raise_real_priority( the_thread, priority );
537    _Thread_Remove_life_change_request( the_thread );
538  }
539
540  _Thread_Dispatch_enable( cpu_self );
541}
542
543void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
544{
545  Thread_queue_Context queue_context;
546
547  _Thread_queue_Context_initialize( &queue_context );
548  _Thread_queue_Context_set_expected_level( &queue_context, 2 );
549  _Thread_State_acquire( the_thread, &queue_context.Lock_context );
550  _Thread_Join(
551    the_thread,
552    STATES_WAITING_FOR_JOIN,
553    executing,
554    &queue_context
555  );
556  _Thread_Cancel( the_thread, executing, NULL );
557}
558
559void _Thread_Exit(
560  Thread_Control    *executing,
561  Thread_Life_state  set,
562  void              *exit_value
563)
564{
565  ISR_lock_Context lock_context;
566
567  _Assert(
568    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
569  );
570  _Assert(
571    executing->current_state == STATES_READY
572      || executing->current_state == STATES_SUSPENDED
573  );
574
575  _Thread_State_acquire( executing, &lock_context );
576  _Thread_Set_exit_value( executing, exit_value );
577  _Thread_Change_life_locked(
578    executing,
579    0,
580    set,
581    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
582  );
583  _Thread_State_release( executing, &lock_context );
584}
585
586bool _Thread_Restart_other(
587  Thread_Control                 *the_thread,
588  const Thread_Entry_information *entry,
589  ISR_lock_Context               *lock_context
590)
591{
592  Thread_Life_state  previous;
593  Per_CPU_Control   *cpu_self;
594
595  _Thread_State_acquire_critical( the_thread, lock_context );
596
597  if ( _States_Is_dormant( the_thread->current_state ) ) {
598    _Thread_State_release( the_thread, lock_context );
599    return false;
600  }
601
602  the_thread->Start.Entry = *entry;
603  previous = _Thread_Change_life_locked(
604    the_thread,
605    0,
606    THREAD_LIFE_RESTARTING,
607    0
608  );
609
610  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
611
612  if ( _Thread_Is_life_change_allowed( previous ) ) {
613    _Thread_Add_life_change_request( the_thread );
614    _Thread_State_release( the_thread, lock_context );
615
616    _Thread_Finalize_life_change(
617      the_thread,
618      the_thread->Start.initial_priority
619    );
620  } else {
621    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
622    _Thread_State_release( the_thread, lock_context );
623  }
624
625  _Thread_Dispatch_enable( cpu_self );
626  return true;
627}
628
629void _Thread_Restart_self(
630  Thread_Control                 *executing,
631  const Thread_Entry_information *entry,
632  ISR_lock_Context               *lock_context
633)
634{
635  Per_CPU_Control  *cpu_self;
636  Priority_Control  unused;
637
638  _Assert(
639    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
640  );
641  _Assert(
642    executing->current_state == STATES_READY
643      || executing->current_state == STATES_SUSPENDED
644  );
645
646  _Thread_State_acquire_critical( executing, lock_context );
647
648  executing->Start.Entry = *entry;
649  _Thread_Change_life_locked(
650    executing,
651    0,
652    THREAD_LIFE_RESTARTING,
653    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
654  );
655
656  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
657  _Thread_State_release( executing, lock_context );
658
659  _Thread_Set_priority(
660    executing,
661    executing->Start.initial_priority,
662    &unused,
663    true
664  );
665
666  _Thread_Dispatch_enable( cpu_self );
667  RTEMS_UNREACHABLE();
668}
669
670Thread_Life_state _Thread_Change_life(
671  Thread_Life_state clear,
672  Thread_Life_state set,
673  Thread_Life_state ignore
674)
675{
676  ISR_lock_Context   lock_context;
677  Thread_Control    *executing;
678  Per_CPU_Control   *cpu_self;
679  Thread_Life_state  previous;
680
681  executing = _Thread_State_acquire_for_executing( &lock_context );
682
683  previous = _Thread_Change_life_locked( executing, clear, set, ignore );
684
685  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
686  _Thread_State_release( executing, &lock_context );
687  _Thread_Dispatch_enable( cpu_self );
688
689  return previous;
690}
691
692Thread_Life_state _Thread_Set_life_protection( Thread_Life_state state )
693{
694  return _Thread_Change_life(
695    THREAD_LIFE_PROTECTED,
696    state & THREAD_LIFE_PROTECTED,
697    0
698  );
699}
Note: See TracBrowser for help on using the repository browser.