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

5
Last change on this file since e41308ea was e41308ea, checked in by Sebastian Huber <sebastian.huber@…>, on 08/22/16 at 08:58:34

score: Introduce Thread_queue_Lock_context

Introduce Thread_queue_Lock_context to contain the context necessary for
thread queue lock and thread wait lock acquire/release operations to
reduce the Thread_Control size.

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