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

Last change on this file since b9083c55 was b9083c55, checked in by Sebastian Huber <sebastian.huber@…>, on 05/14/21 at 12:53:19

rtems: Always set the real priority during restart

Unconditionally set the real priority of the task to its initial
priority during a task restart.

Close #4411.

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