/** * @file * * @brief Changes the Priority of a Thread * * @ingroup ScoreThread */ /* * COPYRIGHT (c) 1989-2014. * On-Line Applications Research Corporation (OAR). * * Copyright (c) 2013, 2016 embedded brains GmbH * * The license and distribution terms for this file may be * found in the file LICENSE in this distribution or at * http://www.rtems.org/license/LICENSE. */ #if HAVE_CONFIG_H #include "config.h" #endif #include #include #include static void _Thread_Set_scheduler_node_priority( Priority_Aggregation *priority_aggregation, bool prepend_it ) { _Scheduler_Node_set_priority( SCHEDULER_NODE_OF_WAIT_PRIORITY_NODE( priority_aggregation ), _Priority_Get_priority( priority_aggregation ), prepend_it ); } #if defined(RTEMS_SMP) static void _Thread_Priority_action_add( Priority_Aggregation *priority_aggregation, Priority_Actions *priority_actions, void *arg ) { Scheduler_Node *scheduler_node; Thread_Control *the_thread; scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY( priority_aggregation ); the_thread = arg; _Thread_Scheduler_add_wait_node( the_thread, scheduler_node ); _Thread_Set_scheduler_node_priority( priority_aggregation, false ); _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_ADD ); _Priority_Actions_add( priority_actions, priority_aggregation ); } static void _Thread_Priority_action_remove( Priority_Aggregation *priority_aggregation, Priority_Actions *priority_actions, void *arg ) { Scheduler_Node *scheduler_node; Thread_Control *the_thread; scheduler_node = SCHEDULER_NODE_OF_WAIT_PRIORITY( priority_aggregation ); the_thread = arg; _Thread_Scheduler_remove_wait_node( the_thread, scheduler_node ); _Thread_Set_scheduler_node_priority( priority_aggregation, true ); _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_REMOVE ); _Priority_Actions_add( priority_actions, priority_aggregation ); } #endif static void _Thread_Priority_action_change( Priority_Aggregation *priority_aggregation, bool prepend_it, Priority_Actions *priority_actions, void *arg ) { _Thread_Set_scheduler_node_priority( priority_aggregation, prepend_it ); #if defined(RTEMS_SMP) || defined(RTEMS_DEBUG) _Priority_Set_action_type( priority_aggregation, PRIORITY_ACTION_CHANGE ); #endif _Priority_Actions_add( priority_actions, priority_aggregation ); } static void _Thread_Priority_do_perform_actions( Thread_Control *the_thread, Thread_queue_Queue *queue, const Thread_queue_Operations *operations, bool prepend_it, Thread_queue_Context *queue_context ) { Priority_Aggregation *priority_aggregation; _Assert( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ); priority_aggregation = _Priority_Actions_move( &queue_context->Priority.Actions ); do { Priority_Aggregation *next_aggregation; Priority_Node *priority_action_node; Priority_Action_type priority_action_type; next_aggregation = _Priority_Get_next_action( priority_aggregation ); priority_action_node = priority_aggregation->Action.node; priority_action_type = priority_aggregation->Action.type; switch ( priority_action_type ) { case PRIORITY_ACTION_ADD: #if defined(RTEMS_SMP) _Priority_Insert( priority_aggregation, priority_action_node, &queue_context->Priority.Actions, _Thread_Priority_action_add, _Thread_Priority_action_change, the_thread ); #else _Priority_Non_empty_insert( priority_aggregation, priority_action_node, &queue_context->Priority.Actions, _Thread_Priority_action_change, NULL ); #endif break; case PRIORITY_ACTION_REMOVE: #if defined(RTEMS_SMP) _Priority_Extract( priority_aggregation, priority_action_node, &queue_context->Priority.Actions, _Thread_Priority_action_remove, _Thread_Priority_action_change, the_thread ); #else _Priority_Extract_non_empty( priority_aggregation, priority_action_node, &queue_context->Priority.Actions, _Thread_Priority_action_change, NULL ); #endif break; default: _Assert( priority_action_type == PRIORITY_ACTION_CHANGE ); _Priority_Changed( priority_aggregation, priority_action_node, prepend_it, &queue_context->Priority.Actions, _Thread_Priority_action_change, NULL ); break; } priority_aggregation = next_aggregation; } while ( _Priority_Actions_is_valid( priority_aggregation ) ); if ( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) { _Thread_queue_Context_add_priority_update( queue_context, the_thread ); ( *operations->priority_actions )( queue, &queue_context->Priority.Actions ); } } void _Thread_Priority_perform_actions( Thread_Control *start_of_path, Thread_queue_Context *queue_context ) { Thread_Control *the_thread; size_t update_count; _Assert( start_of_path != NULL ); /* * This function is tricky on SMP configurations. Please note that we do not * use the thread queue path available via the thread queue context. Instead * we directly use the thread wait information to traverse the thread queue * path. Thus, we do not necessarily acquire all thread queue locks on our * own. In case of a deadlock, we use locks acquired by other processors * along the path. */ the_thread = start_of_path; update_count = _Thread_queue_Context_save_priority_updates( queue_context ); while ( true ) { Thread_queue_Queue *queue; queue = the_thread->Wait.queue; _Thread_Priority_do_perform_actions( the_thread, queue, the_thread->Wait.operations, false, queue_context ); if ( _Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) { return; } _Assert( queue != NULL ); the_thread = queue->owner; _Assert( the_thread != NULL ); /* * In case the priority action list is non-empty, then the current thread * is enqueued on a thread queue. There is no need to notify the scheduler * about a priority change, since it will pick up the new priority once it * is unblocked. Restore the previous set of threads bound to update the * priority. */ _Thread_queue_Context_restore_priority_updates( queue_context, update_count ); } } static void _Thread_Priority_apply( Thread_Control *the_thread, Priority_Node *priority_action_node, Thread_queue_Context *queue_context, bool prepend_it, Priority_Action_type priority_action_type ) { Scheduler_Node *scheduler_node; Thread_queue_Queue *queue; scheduler_node = _Thread_Scheduler_get_home_node( the_thread ); _Priority_Actions_initialize_one( &queue_context->Priority.Actions, &scheduler_node->Wait.Priority, priority_action_node, priority_action_type ); queue = the_thread->Wait.queue; _Thread_Priority_do_perform_actions( the_thread, queue, the_thread->Wait.operations, prepend_it, queue_context ); if ( !_Priority_Actions_is_empty( &queue_context->Priority.Actions ) ) { #if defined(RTEMS_SMP) _Thread_queue_Path_acquire_critical( queue, the_thread, queue_context ); #endif _Thread_Priority_perform_actions( queue->owner, queue_context ); #if defined(RTEMS_SMP) _Thread_queue_Path_release_critical( queue_context ); #endif } } void _Thread_Priority_add( Thread_Control *the_thread, Priority_Node *priority_node, Thread_queue_Context *queue_context ) { _Thread_Priority_apply( the_thread, priority_node, queue_context, false, PRIORITY_ACTION_ADD ); } void _Thread_Priority_remove( Thread_Control *the_thread, Priority_Node *priority_node, Thread_queue_Context *queue_context ) { _Thread_Priority_apply( the_thread, priority_node, queue_context, true, PRIORITY_ACTION_REMOVE ); } void _Thread_Priority_changed( Thread_Control *the_thread, Priority_Node *priority_node, bool prepend_it, Thread_queue_Context *queue_context ) { _Thread_Priority_apply( the_thread, priority_node, queue_context, prepend_it, PRIORITY_ACTION_CHANGE ); } void _Thread_Priority_replace( Thread_Control *the_thread, Priority_Node *victim_node, Priority_Node *replacement_node ) { Scheduler_Node *scheduler_node; scheduler_node = _Thread_Scheduler_get_home_node( the_thread ); _Priority_Replace( &scheduler_node->Wait.Priority, victim_node, replacement_node ); } void _Thread_Priority_update( Thread_queue_Context *queue_context ) { size_t i; size_t n; n = queue_context->Priority.update_count; /* * Update the priority of all threads of the set. Do not care to clear the * set, since the thread queue context will soon get destroyed anyway. */ for ( i = 0; i < n ; ++i ) { Thread_Control *the_thread; ISR_lock_Context lock_context; the_thread = queue_context->Priority.update[ i ]; _Thread_State_acquire( the_thread, &lock_context ); _Scheduler_Update_priority( the_thread ); _Thread_State_release( the_thread, &lock_context ); } } #if defined(RTEMS_SMP) void _Thread_Priority_and_sticky_update( Thread_Control *the_thread, int sticky_level_change ) { ISR_lock_Context lock_context; _Thread_State_acquire( the_thread, &lock_context ); _Scheduler_Priority_and_sticky_update( the_thread, sticky_level_change ); _Thread_State_release( the_thread, &lock_context ); } #endif