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

5
Last change on this file since d78d529 was d78d529, checked in by Sebastian Huber <sebastian.huber@…>, on 11/14/16 at 08:11:07

score: Add and use _Thread_Dispatch_direct()

This function is useful for operations which synchronously block, e.g.
self restart, self deletion, yield, sleep. It helps to detect if these
operations are called in the wrong context. Since the thread dispatch
necessary indicator is not used, this is more robust in some SMP
situations.

Update #2751.

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