source: rtems/cpukit/score/src/threadrestart.c @ 5b6c290

5
Last change on this file since 5b6c290 was 5b6c290, checked in by Sebastian Huber <sebastian.huber@…>, on 12/01/16 at 19:42:48

score: Initialize thread queue context early

Initialize thread queue context early preferably outside the critical
section.

Remove implicit _Thread_queue_Context_initialize() from
_Thread_Wait_acquire().

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