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

5
Last change on this file since 93306058 was 93306058, checked in by Sebastian Huber <sebastian.huber@…>, on 05/27/16 at 12:43:19

score: _CORE_mutex_Check_dispatch_for_seize()

Move the safety check performed by
_CORE_mutex_Check_dispatch_for_seize() out of the performance critical
path and generalize it. Blocking on a thread queue with an unexpected
thread dispatch disabled level is illegal in all system states.

Add the expected thread dispatch disable level (which may be 1 or 2
depending on the operation) to Thread_queue_Context and use it in
_Thread_queue_Enqueue_critical().

  • Property mode set to 100644
File size: 17.1 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(
123    &the_thread->Join_queue,
124    &join_context.Base.Lock_context
125  );
126  _Thread_queue_Flush_critical(
127    &the_thread->Join_queue.Queue,
128    THREAD_JOIN_TQ_OPERATIONS,
129#if defined(RTEMS_POSIX_API)
130    _Thread_Join_flush_filter,
131#else
132    _Thread_queue_Flush_default_filter,
133#endif
134    &join_context.Base
135  );
136}
137
138static void _Thread_Make_zombie( Thread_Control *the_thread )
139{
140  ISR_lock_Context       lock_context;
141  Thread_Zombie_control *zombies;
142
143  if ( _Thread_Owns_resources( the_thread ) ) {
144    _Terminate(
145      INTERNAL_ERROR_CORE,
146      false,
147      INTERNAL_ERROR_RESOURCE_IN_USE
148    );
149  }
150
151  _Objects_Close(
152    _Objects_Get_information_id( the_thread->Object.id ),
153    &the_thread->Object
154  );
155
156  _Thread_Wake_up_joining_threads( the_thread );
157  _Thread_Set_state( the_thread, STATES_ZOMBIE );
158  _Thread_queue_Extract_with_proxy( the_thread );
159  _Thread_Timer_remove( the_thread );
160
161  zombies = &_Thread_Zombies;
162  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
163  _Chain_Append_unprotected( &zombies->Chain, &the_thread->Object.Node );
164  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
165}
166
167static void _Thread_Free( Thread_Control *the_thread )
168{
169  Thread_Information *information = (Thread_Information *)
170    _Objects_Get_information_id( the_thread->Object.id );
171
172  _User_extensions_Thread_delete( the_thread );
173  _User_extensions_Destroy_iterators( the_thread );
174  _ISR_lock_Destroy( &the_thread->Keys.Lock );
175  _Scheduler_Node_destroy( _Scheduler_Get( the_thread ), the_thread );
176  _ISR_lock_Destroy( &the_thread->Timer.Lock );
177
178  /*
179   *  The thread might have been FP.  So deal with that.
180   */
181#if ( CPU_HARDWARE_FP == TRUE ) || ( CPU_SOFTWARE_FP == TRUE )
182#if ( CPU_USE_DEFERRED_FP_SWITCH == TRUE )
183  if ( _Thread_Is_allocated_fp( the_thread ) )
184    _Thread_Deallocate_fp();
185#endif
186
187  _Workspace_Free( the_thread->Start.fp_context );
188#endif
189
190  _Freechain_Put(
191    &information->Free_thread_queue_heads,
192    the_thread->Wait.spare_heads
193  );
194
195  /*
196   *  Free the rest of the memory associated with this task
197   *  and set the associated pointers to NULL for safety.
198   */
199  _Thread_Stack_Free( the_thread );
200
201  _Workspace_Free( the_thread->Start.tls_area );
202
203#if defined(RTEMS_SMP)
204  _SMP_ticket_lock_Destroy( &the_thread->Lock.Default );
205  _SMP_lock_Stats_destroy( &the_thread->Lock.Stats );
206  _SMP_lock_Stats_destroy( &the_thread->Potpourri_stats );
207#endif
208
209  _Thread_queue_Destroy( &the_thread->Join_queue );
210
211  _Objects_Free( &information->Objects, &the_thread->Object );
212}
213
214static void _Thread_Wait_for_execution_stop( Thread_Control *the_thread )
215{
216#if defined(RTEMS_SMP)
217  /*
218   * It is very unlikely that we see an executing thread here.  It can happen
219   * in case the thread termination sequence is interrupted by a slow interrupt
220   * service on a remote processor.
221   */
222  while ( _Thread_Is_executing_on_a_processor( the_thread ) ) {
223    /* Wait */
224  }
225#else
226  (void) the_thread;
227#endif
228}
229
230void _Thread_Kill_zombies( void )
231{
232  ISR_lock_Context lock_context;
233  Thread_Zombie_control *zombies = &_Thread_Zombies;
234  Thread_Control *the_thread;
235
236  _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
237
238  the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
239  while ( the_thread != NULL ) {
240    _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
241
242    _Thread_Wait_for_execution_stop( the_thread );
243    _Thread_Free( the_thread );
244
245    _ISR_lock_ISR_disable_and_acquire( &zombies->Lock, &lock_context );
246
247    the_thread = (Thread_Control *) _Chain_Get_unprotected( &zombies->Chain );
248  }
249
250  _ISR_lock_Release_and_ISR_enable( &zombies->Lock, &lock_context );
251}
252
253static Thread_Life_state _Thread_Change_life_locked(
254  Thread_Control    *the_thread,
255  Thread_Life_state  clear,
256  Thread_Life_state  set,
257  Thread_Life_state  ignore
258)
259{
260  Thread_Life_state previous;
261  Thread_Life_state state;
262
263  previous = the_thread->Life.state;
264  state = previous;
265  state &= ~clear;
266  state |= set;
267  the_thread->Life.state = state;
268
269  state &= ~ignore;
270
271  if (
272    _Thread_Is_life_change_allowed( state )
273      && _Thread_Is_life_changing( state )
274  ) {
275    the_thread->is_preemptible   = the_thread->Start.is_preemptible;
276    the_thread->budget_algorithm = the_thread->Start.budget_algorithm;
277    the_thread->budget_callout   = the_thread->Start.budget_callout;
278
279    _Thread_Add_post_switch_action(
280      the_thread,
281      &the_thread->Life.Action,
282      _Thread_Life_action_handler
283    );
284  }
285
286  return previous;
287}
288
289static Per_CPU_Control *_Thread_Wait_for_join(
290  Thread_Control  *executing,
291  Per_CPU_Control *cpu_self
292)
293{
294#if defined(RTEMS_POSIX_API)
295  ISR_lock_Context lock_context;
296
297  _Thread_State_acquire( executing, &lock_context );
298
299  if (
300    _Thread_Is_joinable( executing )
301      && _Thread_queue_Is_empty( &executing->Join_queue.Queue )
302  ) {
303    _Thread_Set_state_locked( executing, STATES_WAITING_FOR_JOIN_AT_EXIT );
304    _Thread_State_release( executing, &lock_context );
305    _Thread_Dispatch_enable( cpu_self );
306
307    /* Let other threads run */
308
309    cpu_self = _Thread_Dispatch_disable();
310  } else {
311    _Thread_State_release( executing, &lock_context );
312  }
313#endif
314
315  return cpu_self;
316}
317
318void _Thread_Life_action_handler(
319  Thread_Control   *executing,
320  Thread_Action    *action,
321  ISR_lock_Context *lock_context
322)
323{
324  Thread_Life_state  previous_life_state;
325  Per_CPU_Control   *cpu_self;
326
327  (void) action;
328
329  previous_life_state = executing->Life.state;
330  executing->Life.state = previous_life_state | THREAD_LIFE_PROTECTED;
331
332  _Thread_State_release( executing, lock_context );
333
334  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
335    _User_extensions_Thread_terminate( executing );
336  } else {
337    _Assert( _Thread_Is_life_restarting( previous_life_state ) );
338
339    _User_extensions_Thread_restart( executing );
340  }
341
342  cpu_self = _Thread_Dispatch_disable();
343
344  if ( _Thread_Is_life_terminating( previous_life_state ) ) {
345    cpu_self = _Thread_Wait_for_join( executing, cpu_self );
346
347    _Thread_Make_zombie( executing );
348
349    _Thread_Dispatch_enable( 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_Change_priority(
436    the_thread,
437    priority,
438    NULL,
439    _Thread_Raise_real_priority_filter,
440    false
441  );
442  _Thread_Remove_life_change_request( the_thread );
443}
444
445void _Thread_Join(
446  Thread_Control       *the_thread,
447  States_Control        waiting_for_join,
448  Thread_Control       *executing,
449  Thread_queue_Context *queue_context
450)
451{
452  _Assert( the_thread != executing );
453  _Assert( _Thread_State_is_owner( the_thread ) );
454
455#if defined(RTEMS_POSIX_API)
456  executing->Wait.return_argument = NULL;
457#endif
458
459  _Thread_queue_Enqueue_critical(
460    &the_thread->Join_queue.Queue,
461    THREAD_JOIN_TQ_OPERATIONS,
462    executing,
463    waiting_for_join,
464    WATCHDOG_NO_TIMEOUT,
465    queue_context
466  );
467}
468
469static void _Thread_Set_exit_value(
470  Thread_Control *the_thread,
471  void           *exit_value
472)
473{
474#if defined(RTEMS_POSIX_API)
475  the_thread->Life.exit_value = exit_value;
476#endif
477}
478
479void _Thread_Cancel(
480  Thread_Control *the_thread,
481  Thread_Control *executing,
482  void           *exit_value
483)
484{
485  ISR_lock_Context   lock_context;
486  Thread_Life_state  previous;
487  Per_CPU_Control   *cpu_self;
488  Priority_Control   priority;
489
490  _Assert( the_thread != executing );
491
492  _Thread_State_acquire( the_thread, &lock_context );
493
494  _Thread_Set_exit_value( the_thread, exit_value );
495  previous = _Thread_Change_life_locked(
496    the_thread,
497    0,
498    THREAD_LIFE_TERMINATING,
499    0
500  );
501
502  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
503  priority = executing->current_priority;
504
505  if ( _States_Is_dormant( the_thread->current_state ) ) {
506    _Thread_State_release( the_thread, &lock_context );
507    _Thread_Make_zombie( the_thread );
508  } else if ( _Thread_Is_life_change_allowed( previous ) ) {
509    _Thread_Add_life_change_request( the_thread );
510    _Thread_State_release( the_thread, &lock_context );
511
512    _Thread_Finalize_life_change( the_thread, priority );
513  } else {
514    _Thread_Add_life_change_request( the_thread );
515    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
516    _Thread_State_release( the_thread, &lock_context );
517
518    _Thread_Raise_real_priority( the_thread, priority );
519    _Thread_Remove_life_change_request( the_thread );
520  }
521
522  _Thread_Dispatch_enable( cpu_self );
523}
524
525void _Thread_Close( Thread_Control *the_thread, Thread_Control *executing )
526{
527  Thread_queue_Context queue_context;
528
529  _Thread_queue_Context_initialize( &queue_context );
530  _Thread_queue_Context_set_expected_level( &queue_context, 2 );
531  _Thread_State_acquire( the_thread, &queue_context.Lock_context );
532  _Thread_Join(
533    the_thread,
534    STATES_WAITING_FOR_JOIN,
535    executing,
536    &queue_context
537  );
538  _Thread_Cancel( the_thread, executing, NULL );
539}
540
541void _Thread_Exit(
542  Thread_Control    *executing,
543  Thread_Life_state  set,
544  void              *exit_value
545)
546{
547  ISR_lock_Context lock_context;
548
549  _Assert(
550    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
551  );
552  _Assert(
553    executing->current_state == STATES_READY
554      || executing->current_state == STATES_SUSPENDED
555  );
556
557  _Thread_State_acquire( executing, &lock_context );
558  _Thread_Set_exit_value( executing, exit_value );
559  _Thread_Change_life_locked(
560    executing,
561    0,
562    set,
563    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
564  );
565  _Thread_State_release( executing, &lock_context );
566}
567
568bool _Thread_Restart_other(
569  Thread_Control                 *the_thread,
570  const Thread_Entry_information *entry,
571  ISR_lock_Context               *lock_context
572)
573{
574  Thread_Life_state  previous;
575  Per_CPU_Control   *cpu_self;
576
577  _Thread_State_acquire_critical( the_thread, lock_context );
578
579  if ( _States_Is_dormant( the_thread->current_state ) ) {
580    _Thread_State_release( the_thread, lock_context );
581    return false;
582  }
583
584  the_thread->Start.Entry = *entry;
585  previous = _Thread_Change_life_locked(
586    the_thread,
587    0,
588    THREAD_LIFE_RESTARTING,
589    0
590  );
591
592  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
593
594  if ( _Thread_Is_life_change_allowed( previous ) ) {
595    _Thread_Add_life_change_request( the_thread );
596    _Thread_State_release( the_thread, lock_context );
597
598    _Thread_Finalize_life_change(
599      the_thread,
600      the_thread->Start.initial_priority
601    );
602  } else {
603    _Thread_Clear_state_locked( the_thread, STATES_SUSPENDED );
604    _Thread_State_release( the_thread, lock_context );
605  }
606
607  _Thread_Dispatch_enable( cpu_self );
608  return true;
609}
610
611void _Thread_Restart_self(
612  Thread_Control                 *executing,
613  const Thread_Entry_information *entry,
614  ISR_lock_Context               *lock_context
615)
616{
617  Per_CPU_Control  *cpu_self;
618  Priority_Control  unused;
619
620  _Assert(
621    _Watchdog_Get_state( &executing->Timer.Watchdog ) == WATCHDOG_INACTIVE
622  );
623  _Assert(
624    executing->current_state == STATES_READY
625      || executing->current_state == STATES_SUSPENDED
626  );
627
628  _Thread_State_acquire_critical( executing, lock_context );
629
630  executing->Start.Entry = *entry;
631  _Thread_Change_life_locked(
632    executing,
633    0,
634    THREAD_LIFE_RESTARTING,
635    THREAD_LIFE_PROTECTED | THREAD_LIFE_CHANGE_DEFERRED
636  );
637
638  cpu_self = _Thread_Dispatch_disable_critical( lock_context );
639  _Thread_State_release( executing, lock_context );
640
641  _Thread_Set_priority(
642    executing,
643    executing->Start.initial_priority,
644    &unused,
645    true
646  );
647
648  _Thread_Dispatch_enable( cpu_self );
649  RTEMS_UNREACHABLE();
650}
651
652Thread_Life_state _Thread_Change_life(
653  Thread_Life_state clear,
654  Thread_Life_state set,
655  Thread_Life_state ignore
656)
657{
658  ISR_lock_Context   lock_context;
659  Thread_Control    *executing;
660  Per_CPU_Control   *cpu_self;
661  Thread_Life_state  previous;
662
663  executing = _Thread_State_acquire_for_executing( &lock_context );
664
665  previous = _Thread_Change_life_locked( executing, clear, set, ignore );
666
667  cpu_self = _Thread_Dispatch_disable_critical( &lock_context );
668  _Thread_State_release( executing, &lock_context );
669  _Thread_Dispatch_enable( cpu_self );
670
671  return previous;
672}
673
674Thread_Life_state _Thread_Set_life_protection( Thread_Life_state state )
675{
676  return _Thread_Change_life(
677    THREAD_LIFE_PROTECTED,
678    state & THREAD_LIFE_PROTECTED,
679    0
680  );
681}
Note: See TracBrowser for help on using the repository browser.