diff options
Diffstat (limited to 'cpukit/score/src/threadprioritynode.c')
-rw-r--r-- | cpukit/score/src/threadprioritynode.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/cpukit/score/src/threadprioritynode.c b/cpukit/score/src/threadprioritynode.c new file mode 100644 index 0000000000..69bbe14972 --- /dev/null +++ b/cpukit/score/src/threadprioritynode.c @@ -0,0 +1,167 @@ +/* + * Thread Priority Node + * + * Copyright (C) 2017. Gedare Bloom. + * + * 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 <rtems/system.h> +#include <rtems/config.h> +#include <rtems/score/chain.h> +#include <rtems/score/coremutex.h> +#include <rtems/score/thread.h> + +/** + * This routine adds a thread's Priority_node to the mutex holder's + * Inherited_priorities list, and sets the waiting_to_hold field to the + * mutex. + * + * Returns true if the enqueued node is at the head of the holder's + * Inherited priorities, and therefore the holder's priority may change. + */ +bool _Thread_Enqueue_priority_node( + Thread_Control *the_thread, + CORE_mutex_Control *the_mutex +) +{ + Chain_Node *iter; + Thread_Priority_node *thread_pn, *holder_pn; + Priority_Control task_priority, iter_priority; + + thread_pn = &the_thread->Priority_node; + holder_pn = &the_mutex->holder->Priority_node; + + thread_pn->waiting_to_hold = the_mutex; + iter = holder_pn->Inherited_priorities.first; + task_priority = thread_pn->current_priority; + + /* Find the first node in holder's Inherited_priorities list with + * lower priority than the task, or find the end of the list. */ + while ( !_Chain_Is_tail( &holder_pn->Inherited_priorities, iter) ) { + iter_priority = ((Thread_Priority_node *)iter)->current_priority; + if ( iter_priority > task_priority ) { + break; + } + iter = _Chain_Next( iter ); + } + _Chain_Insert_unprotected( _Chain_Previous( iter ), &thread_pn->Node ); + + /* If the_thread is at the start of the holder's Inherited_priorites list, + * then may need to update the holder's current priority. */ + if ( _Chain_Is_first( &thread_pn->Node ) ) { + return true; + } + return false; +} + +/** + * This routine sets the thread's priority_node->current_priority to the + * minimum of the real_priority and the first node of Inherited_priorities, + * if any. + */ +void _Thread_Evaluate_priority( + Thread_Control *the_thread +) +{ + Chain_Node *first; + Thread_Priority_node *first_pn, *priority_node; + Priority_Control current_priority, min_priority; + + /* Start with the_thread, and iterate through the holder threads that + * the_thread is waiting on. If any thread's priority does not change, + * then stop iterating. */ + while ( the_thread != NULL ) { + priority_node = &the_thread->Priority_node; + current_priority = priority_node->current_priority; + min_priority = priority_node->real_priority; + first = _Chain_First( &priority_node->Inherited_priorities ); + if ( !_Chain_Is_tail( &priority_node->Inherited_priorities, first ) ) { + first_pn = (Thread_Priority_node *)first; + if ( min_priority > first_pn->current_priority ) { + min_priority = first_pn->current_priority; + } + } + if ( current_priority < min_priority ) { + _Thread_Change_priority( the_thread, min_priority, true ); + } else if ( current_priority > min_priority ) { + _Thread_Change_priority( the_thread, min_priority, false ); + } else { + break; + } + if ( the_thread->Priority_node.waiting_to_hold != NULL ) { + the_thread = the_thread->Priority_node.waiting_to_hold->holder; + } else { + the_thread = NULL; + } + } +} + +/** + * This routine removes a thread's Priority_node from an + * Inherited_priorities list, updates the mutex holder's + * current_priority if needed, and clears the mutex field. + * + * Returns the mutex that the_thread was waiting on. + */ +CORE_mutex_Control* _Thread_Dequeue_priority_node( + Thread_Priority_node *thread_pn +) +{ + CORE_mutex_Control *the_mutex = thread_pn->waiting_to_hold; + + if ( the_mutex == NULL ) { + return NULL; + } + + _Chain_Extract_unprotected( &thread_pn->Node ); + thread_pn->waiting_to_hold = NULL; + + return the_mutex; +} + +void _Thread_Requeue_priority_node( + Thread_Control *the_thread +) +{ + ISR_Level level; + CORE_mutex_Control *mutex; + + _ISR_Disable( level ); + /* TODO: refactor? */ + mutex = _Thread_Dequeue_priority_node( &the_thread->Priority_node ); + if ( mutex != NULL ) { + _Thread_Enqueue_priority_node( the_thread, mutex ); + } + _ISR_Enable( level ); +} + +/** + * This routine removes the inherited priorities from the mutex being + * released and updates the holder's priority if necessary. + */ +void _Thread_Release_inherited_priority( + CORE_mutex_Control *the_mutex +) +{ + Chain_Control *ip = &the_mutex->holder->Priority_node.Inherited_priorities; + Chain_Node *cn; + Thread_Priority_node *tpn; + + for (cn = _Chain_First(ip); !_Chain_Is_tail(ip, cn); cn = _Chain_Next(cn)) { + tpn = (Thread_Priority_node*)cn; + if ( tpn->waiting_to_hold == the_mutex ) { + _Chain_Extract(cn); + tpn->waiting_to_hold = NULL; + } + } + + _Thread_Evaluate_priority( the_mutex->holder ); +} + |