diff options
-rw-r--r-- | cpukit/rtems/src/signalsend.c | 98 |
1 files changed, 91 insertions, 7 deletions
diff --git a/cpukit/rtems/src/signalsend.c b/cpukit/rtems/src/signalsend.c index 606ddfcb53..8c4ce10788 100644 --- a/cpukit/rtems/src/signalsend.c +++ b/cpukit/rtems/src/signalsend.c @@ -21,7 +21,9 @@ #endif #include <rtems/rtems/signalimpl.h> +#include <rtems/rtems/modesimpl.h> #include <rtems/rtems/tasksdata.h> +#include <rtems/score/schedulerimpl.h> #include <rtems/score/threaddispatch.h> #include <rtems/score/threadimpl.h> @@ -33,13 +35,29 @@ static void _Signal_Action_handler( ISR_lock_Context *lock_context ) { - RTEMS_API_Control *api; - ASR_Information *asr; - rtems_signal_set signal_set; - rtems_mode prev_mode; + RTEMS_API_Control *api; + ASR_Information *asr; + rtems_signal_set signal_set; + bool normal_is_preemptible; + uint32_t normal_cpu_time_budget; + Thread_CPU_budget_algorithms normal_budget_algorithm; + bool normal_asr_is_enabled; + uint32_t normal_isr_level; + uint32_t before_call_isr_level; + bool after_call_is_preemptible; + bool after_call_asr_is_enabled; (void) action; + /* + * For the variable names the following notation is used. The prefix + * "normal" specifies the mode associated with the normal task execution. + * The prefix "before_call" specifies the mode set up right before the ASR + * handler is called. The prefix "after_call" specifies the mode after the + * ASR handler call returned. This mode may differ from the "before_call" + * mode since an ASR handler is free to change the task mode. + */ + api = executing->API_Extensions[ THREAD_API_RTEMS ]; asr = &api->Signal; @@ -48,16 +66,82 @@ static void _Signal_Action_handler( _Assert( signal_set != 0 ); asr->signals_pending = 0; - _Thread_State_release( executing, lock_context ); + /* Save normal mode */ + + normal_is_preemptible = executing->is_preemptible; + normal_asr_is_enabled = asr->is_enabled; + normal_cpu_time_budget = executing->cpu_time_budget; + normal_budget_algorithm = executing->budget_algorithm; + + /* Set mode for ASR processing */ + + executing->is_preemptible = _Modes_Is_preempt( asr->mode_set ); + asr->is_enabled = !_Modes_Is_asr_disabled( asr->mode_set ); + _Modes_Apply_timeslice_to_thread( asr->mode_set, executing ); + before_call_isr_level = _Modes_Get_interrupt_level( asr->mode_set ); + + if ( executing->is_preemptible && !normal_is_preemptible ) { + Per_CPU_Control *cpu_self; + + cpu_self = _Thread_Dispatch_disable_critical( lock_context ); + _Scheduler_Schedule( executing ); + _Thread_State_release( executing, lock_context ); + _Thread_Dispatch_direct( cpu_self ); + } else { + _Thread_State_release( executing, lock_context ); + } - rtems_task_mode( asr->mode_set, RTEMS_ALL_MODE_MASKS, &prev_mode ); + normal_isr_level = _ISR_Get_level(); + _ISR_Set_level( before_call_isr_level ); /* Call the ASR handler in the ASR processing mode */ ( *asr->handler )( signal_set ); - rtems_task_mode( prev_mode, RTEMS_ALL_MODE_MASKS, &prev_mode ); + /* Restore normal mode */ + + _ISR_Set_level( normal_isr_level ); _Thread_State_acquire( executing, lock_context ); + + executing->cpu_time_budget = normal_cpu_time_budget ; + executing->budget_algorithm = normal_budget_algorithm ; + after_call_is_preemptible = executing->is_preemptible; + executing->is_preemptible = normal_is_preemptible; + + /* + * We do the best to avoid recursion in the ASR processing. A well behaved + * application will disable ASR processing during ASR processing. In this + * case, ASR processing is currently disabled. We do now the thread dispatch + * necessary due to a re-enabled preemption mode. This helps to avoid doing + * the next round of ASR processing recursively in _Thread_Dispatch_direct(). + */ + if ( normal_is_preemptible && !after_call_is_preemptible ) { + Per_CPU_Control *cpu_self; + + cpu_self = _Thread_Dispatch_disable_critical( lock_context ); + _Scheduler_Schedule( executing ); + _Thread_State_release( executing, lock_context ); + _Thread_Dispatch_direct( cpu_self ); + _Thread_State_acquire( executing, lock_context ); + } + + /* + * Restore the normal ASR processing mode. If we enable ASR processing and + * there are pending signals, then add us as a post-switch action. The loop + * in _Thread_Run_post_switch_actions() will continue after our return and + * call us again. This avoids a recursion. + */ + + after_call_asr_is_enabled = asr->is_enabled; + asr->is_enabled = normal_asr_is_enabled; + + if ( + normal_asr_is_enabled && + !after_call_asr_is_enabled && + asr->signals_pending != 0 + ) { + _Thread_Append_post_switch_action( executing, action ); + } } rtems_status_code rtems_signal_send( |