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

Last change on this file was 1df822a, checked in by Sebastian Huber <sebastian.huber@…>, on 03/20/24 at 15:23:08

Mark parameters as intentionally unused

The parameters are unused due to API constraints. The functions are
used through function pointers. Alternative implementations may use the
parameters.

Update #4862.

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