source: rtems/cpukit/score/src/threadrestart.c @ 57be57c7

Last change on this file since 57be57c7 was 57be57c7, checked in by Sebastian Huber <sebastian.huber@…>, on May 14, 2021 at 7:31:47 AM

score: Return status in _Thread_Restart_other()

This simplifies rtems_task_restart().

  • Property mode set to 100644
File size: 17.0 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
378static void _Thread_Finalize_life_change(
379  Thread_Control   *the_thread,
380  Priority_Control  priority
381)
382{
383  _Thread_queue_Extract_with_proxy( the_thread );
384  _Thread_Timer_remove( the_thread );
385  _Thread_Raise_real_priority( the_thread, priority );
386  _Thread_Remove_life_change_request( the_thread );
387}
388
389void _Thread_Join(
390  Thread_Control       *the_thread,
391  States_Control        waiting_for_join,
392  Thread_Control       *executing,
393  Thread_queue_Context *queue_context
394)
395{
396  _Assert( the_thread != executing );
397  _Assert( _Thread_State_is_owner( the_thread ) );
398
399  executing->Wait.return_argument = NULL;
400
401  _Thread_queue_Context_set_thread_state( queue_context, waiting_for_join );
402  _Thread_queue_Enqueue(
403    &the_thread->Join_queue.Queue,
404    THREAD_JOIN_TQ_OPERATIONS,
405    executing,
406    queue_context
407  );
408}
409
410static void _Thread_Set_exit_value(
411  Thread_Control *the_thread,
412  void           *exit_value
413)
414{
415  the_thread->Life.exit_value = exit_value;
416}
417
418void _Thread_Cancel(
419  Thread_Control *the_thread,
420  Thread_Control *executing,
421  void           *exit_value
422)
423{
424  ISR_lock_Context   lock_context;
425  Thread_Life_state  previous;
426  Per_CPU_Control   *cpu_self;
427  Priority_Control   priority;
428
429  _Assert( the_thread != executing );
430
431  _Thread_State_acquire( the_thread, &lock_context );
432
433  _Thread_Set_exit_value( the_thread, exit_value );
434  previous = _Thread_Change_life_locked(
435    the_thread,
436    0,
437    THREAD_LIFE_TERMINATING,
438    0
439  );
440
441  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
442  priority = _Thread_Get_priority( executing );
443
444  if ( _States_Is_dormant( the_thread->current_state ) ) {
445    _Thread_State_release( the_thread, &lock_context );
446    _Thread_Make_zombie( the_thread );
447  } else if ( _Thread_Is_life_change_allowed( previous ) ) {
448    _Thread_Add_life_change_request( the_thread );
449    _Thread_State_release( the_thread, &lock_context );
450
451    _Thread_Finalize_life_change( the_thread, priority );
452  } else {
453    _Thread_Add_life_change_request( the_thread );
454    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
455    _Thread_State_release( the_thread, &lock_context );
456
457    _Thread_Raise_real_priority( the_thread, priority );
458    _Thread_Remove_life_change_request( the_thread );
459  }
460
461  _Thread_Dispatch_enable( cpu_self );
462}
463
464static void _Thread_Close_enqueue_callout(
465  Thread_queue_Queue   *queue,
466  Thread_Control       *the_thread,
467  Per_CPU_Control      *cpu_self,
468  Thread_queue_Context *queue_context
469)
470{
471  Thread_Close_context *context;
472
473  context = (Thread_Close_context *) queue_context;
474  _Thread_Cancel( context->cancel, the_thread, NULL );
475}
476
477void _Thread_Close(
478  Thread_Control       *the_thread,
479  Thread_Control       *executing,
480  Thread_Close_context *context
481)
482{
483  context->cancel = the_thread;
484  _Thread_queue_Context_set_enqueue_callout(
485    &context->Base,
486    _Thread_Close_enqueue_callout
487  );
488  _Thread_State_acquire_critical(
489    the_thread,
490    &context->Base.Lock_context.Lock_context
491  );
492  _Thread_Join(
493    the_thread,
494    STATES_WAITING_FOR_JOIN,
495    executing,
496    &context->Base
497  );
498}
499
500void _Thread_Exit(
501  Thread_Control    *executing,
502  Thread_Life_state  set,
503  void              *exit_value
504)
505{
506  ISR_lock_Context lock_context;
507
508  _Assert(
509    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
510  );
511  _Assert(
512    executing->current_state == STATES_READY
513      || executing->current_state == STATES_SUSPENDED
514  );
515
516  _Thread_State_acquire( executing, &lock_context );
517  _Thread_Set_exit_value( executing, exit_value );
518  _Thread_Change_life_locked(
519    executing,
520    0,
521    set,
522    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
523  );
524  _Thread_State_release( executing, &lock_context );
525}
526
527Status_Control _Thread_Restart_other(
528  Thread_Control                 *the_thread,
529  const Thread_Entry_information *entry,
530  ISR_lock_Context               *lock_context
531)
532{
533  Thread_Life_state  previous;
534  Per_CPU_Control   *cpu_self;
535
536  _Thread_State_acquire_critical( the_thread, lock_context );
537
538  if ( _States_Is_dormant( the_thread->current_state ) ) {
539    _Thread_State_release( the_thread, lock_context );
540    return STATUS_INCORRECT_STATE;
541  }
542
543  the_thread->Start.Entry = *entry;
544  previous = _Thread_Change_life_locked(
545    the_thread,
546    0,
547    THREAD_LIFE_RESTARTING,
548    0
549  );
550
551  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
552
553  if ( _Thread_Is_life_change_allowed( previous ) ) {
554    _Thread_Add_life_change_request( the_thread );
555    _Thread_State_release( the_thread, lock_context );
556
557    _Thread_Finalize_life_change(
558      the_thread,
559      the_thread->Start.initial_priority
560    );
561  } else {
562    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
563    _Thread_State_release( the_thread, lock_context );
564  }
565
566  _Thread_Dispatch_enable( cpu_self );
567  return STATUS_SUCCESSFUL;
568}
569
570void _Thread_Restart_self(
571  Thread_Control                 *executing,
572  const Thread_Entry_information *entry,
573  ISR_lock_Context               *lock_context
574)
575{
576  Per_CPU_Control      *cpu_self;
577  Thread_queue_Context  queue_context;
578
579  _Assert(
580    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
581  );
582  _Assert(
583    executing->current_state == STATES_READY
584      || executing->current_state == STATES_SUSPENDED
585  );
586
587  _Thread_queue_Context_initialize( &queue_context );
588  _Thread_queue_Context_clear_priority_updates( &queue_context );
589  _Thread_State_acquire_critical( executing, lock_context );
590
591  executing->Start.Entry = *entry;
592  _Thread_Change_life_locked(
593    executing,
594    0,
595    THREAD_LIFE_RESTARTING,
596    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
597  );
598
599  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
600  _Thread_State_release( executing, lock_context );
601
602  _Thread_Wait_acquire_default( executing, lock_context );
603  _Thread_Priority_change(
604    executing,
605    &executing->Real_priority,
606    executing->Start.initial_priority,
607    false,
608    &queue_context
609  );
610  _Thread_Wait_release_default( executing, lock_context );
611
612  _Thread_Priority_update( &queue_context );
613  _Thread_Dispatch_direct_no_return( cpu_self );
614  RTEMS_UNREACHABLE();
615}
616
617Thread_Life_state _Thread_Change_life(
618  Thread_Life_state clear,
619  Thread_Life_state set,
620  Thread_Life_state ignore
621)
622{
623  ISR_lock_Context   lock_context;
624  Thread_Control    *executing;
625  Per_CPU_Control   *cpu_self;
626  Thread_Life_state  previous;
627
628  executing = _Thread_State_acquire_for_executing( &lock_context );
629
630  previous = _Thread_Change_life_locked( executing, clear, set, ignore );
631
632  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
633  _Thread_State_release( executing, &lock_context );
634  _Thread_Dispatch_enable( cpu_self );
635
636  return previous;
637}
638
639Thread_Life_state _Thread_Set_life_protection( Thread_Life_state state )
640{
641  return _Thread_Change_life(
642    THREAD_LIFE_PROTECTED,
643    state & THREAD_LIFE_PROTECTED,
644    0
645  );
646}
Note: See TracBrowser for help on using the repository browser.