diff options
Diffstat (limited to 'cpukit/score/include/rtems/score/schedulerimpl.h')
-rw-r--r-- | cpukit/score/include/rtems/score/schedulerimpl.h | 1203 |
1 files changed, 0 insertions, 1203 deletions
diff --git a/cpukit/score/include/rtems/score/schedulerimpl.h b/cpukit/score/include/rtems/score/schedulerimpl.h deleted file mode 100644 index 10c12242a9..0000000000 --- a/cpukit/score/include/rtems/score/schedulerimpl.h +++ /dev/null @@ -1,1203 +0,0 @@ -/** - * @file - * - * @brief Inlined Routines Associated with the Manipulation of the Scheduler - * - * This inline file contains all of the inlined routines associated with - * the manipulation of the scheduler. - */ - -/* - * Copyright (C) 2010 Gedare Bloom. - * Copyright (C) 2011 On-Line Applications Research Corporation (OAR). - * Copyright (c) 2014, 2017 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. - */ - -#ifndef _RTEMS_SCORE_SCHEDULERIMPL_H -#define _RTEMS_SCORE_SCHEDULERIMPL_H - -#include <rtems/score/scheduler.h> -#include <rtems/score/assert.h> -#include <rtems/score/priorityimpl.h> -#include <rtems/score/smpimpl.h> -#include <rtems/score/status.h> -#include <rtems/score/threadimpl.h> - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @addtogroup ScoreScheduler - */ -/**@{**/ - -/** - * @brief Maps a priority value to support the append indicator. - */ -#define SCHEDULER_PRIORITY_MAP( priority ) ( ( priority ) << 1 ) - -/** - * @brief Returns the plain priority value. - */ -#define SCHEDULER_PRIORITY_UNMAP( priority ) ( ( priority ) >> 1 ) - -/** - * @brief Clears the priority append indicator bit. - */ -#define SCHEDULER_PRIORITY_PURIFY( priority ) \ - ( ( priority ) & ~( (Priority_Control) SCHEDULER_PRIORITY_APPEND_FLAG ) ) - -/** - * @brief Returns the priority control with the append indicator bit set. - */ -#define SCHEDULER_PRIORITY_APPEND( priority ) \ - ( ( priority ) | SCHEDULER_PRIORITY_APPEND_FLAG ) - -/** - * @brief Returns true, if the item should be appended to its priority group, - * otherwise returns false and the item should be prepended to its priority - * group. - */ -#define SCHEDULER_PRIORITY_IS_APPEND( priority ) \ - ( ( ( priority ) & SCHEDULER_PRIORITY_APPEND_FLAG ) != 0 ) - -/** - * @brief Initializes the scheduler to the policy chosen by the user. - * - * This routine initializes the scheduler to the policy chosen by the user - * through confdefs, or to the priority scheduler with ready chains by - * default. - */ -void _Scheduler_Handler_initialization( void ); - -RTEMS_INLINE_ROUTINE Scheduler_Context *_Scheduler_Get_context( - const Scheduler_Control *scheduler -) -{ - return scheduler->context; -} - -RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get_by_CPU( - const Per_CPU_Control *cpu -) -{ -#if defined(RTEMS_SMP) - return cpu->Scheduler.control; -#else - (void) cpu; - return &_Scheduler_Table[ 0 ]; -#endif -} - -/** - * @brief Acquires the scheduler instance inside a critical section (interrupts - * disabled). - * - * @param[in] scheduler The scheduler instance. - * @param[in] lock_context The lock context to use for - * _Scheduler_Release_critical(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Acquire_critical( - const Scheduler_Control *scheduler, - ISR_lock_Context *lock_context -) -{ -#if defined(RTEMS_SMP) - Scheduler_Context *context; - - context = _Scheduler_Get_context( scheduler ); - _ISR_lock_Acquire( &context->Lock, lock_context ); -#else - (void) scheduler; - (void) lock_context; -#endif -} - -/** - * @brief Releases the scheduler instance inside a critical section (interrupts - * disabled). - * - * @param[in] scheduler The scheduler instance. - * @param[in] lock_context The lock context used for - * _Scheduler_Acquire_critical(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Release_critical( - const Scheduler_Control *scheduler, - ISR_lock_Context *lock_context -) -{ -#if defined(RTEMS_SMP) - Scheduler_Context *context; - - context = _Scheduler_Get_context( scheduler ); - _ISR_lock_Release( &context->Lock, lock_context ); -#else - (void) scheduler; - (void) lock_context; -#endif -} - -#if defined(RTEMS_SMP) -void _Scheduler_Request_ask_for_help( Thread_Control *the_thread ); - -/** - * @brief Registers an ask for help request if necessary. - * - * The actual ask for help operation is carried out during - * _Thread_Do_dispatch() on a processor related to the thread. This yields a - * better separation of scheduler instances. A thread of one scheduler - * instance should not be forced to carry out too much work for threads on - * other scheduler instances. - * - * @param[in] the_thread The thread in need for help. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Ask_for_help( Thread_Control *the_thread ) -{ - _Assert( _Thread_State_is_owner( the_thread ) ); - - if ( the_thread->Scheduler.helping_nodes > 0 ) { - _Scheduler_Request_ask_for_help( the_thread ); - } -} -#endif - -/** - * The preferred method to add a new scheduler is to define the jump table - * entries and add a case to the _Scheduler_Initialize routine. - * - * Generic scheduling implementations that rely on the ready queue only can - * be found in the _Scheduler_queue_XXX functions. - */ - -/* - * Passing the Scheduler_Control* to these functions allows for multiple - * scheduler's to exist simultaneously, which could be useful on an SMP - * system. Then remote Schedulers may be accessible. How to protect such - * accesses remains an open problem. - */ - -/** - * @brief General scheduling decision. - * - * This kernel routine implements the scheduling decision logic for - * the scheduler. It does NOT dispatch. - * - * @param[in] the_thread The thread which state changed previously. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Schedule( Thread_Control *the_thread ) -{ - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - scheduler = _Thread_Scheduler_get_home( the_thread ); - _Scheduler_Acquire_critical( scheduler, &lock_context ); - - ( *scheduler->Operations.schedule )( scheduler, the_thread ); - - _Scheduler_Release_critical( scheduler, &lock_context ); -} - -/** - * @brief Scheduler yield with a particular thread. - * - * This routine is invoked when a thread wishes to voluntarily transfer control - * of the processor to another thread. - * - * @param[in] the_thread The yielding thread. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Yield( Thread_Control *the_thread ) -{ - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - scheduler = _Thread_Scheduler_get_home( the_thread ); - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.yield )( - scheduler, - the_thread, - _Thread_Scheduler_get_home_node( the_thread ) - ); - _Scheduler_Release_critical( scheduler, &lock_context ); -} - -/** - * @brief Blocks a thread with respect to the scheduler. - * - * This routine removes @a the_thread from the scheduling decision for - * the scheduler. The primary task is to remove the thread from the - * ready queue. It performs any necessary schedulering operations - * including the selection of a new heir thread. - * - * @param[in] the_thread The thread. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Block( Thread_Control *the_thread ) -{ -#if defined(RTEMS_SMP) - Chain_Node *node; - const Chain_Node *tail; - Scheduler_Node *scheduler_node; - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes ); - tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes ); - - scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); - scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); - - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.block )( - scheduler, - the_thread, - scheduler_node - ); - _Scheduler_Release_critical( scheduler, &lock_context ); - - node = _Chain_Next( node ); - - while ( node != tail ) { - scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); - scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); - - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.withdraw_node )( - scheduler, - the_thread, - scheduler_node, - THREAD_SCHEDULER_BLOCKED - ); - _Scheduler_Release_critical( scheduler, &lock_context ); - - node = _Chain_Next( node ); - } -#else - const Scheduler_Control *scheduler; - - scheduler = _Thread_Scheduler_get_home( the_thread ); - ( *scheduler->Operations.block )( - scheduler, - the_thread, - _Thread_Scheduler_get_home_node( the_thread ) - ); -#endif -} - -/** - * @brief Unblocks a thread with respect to the scheduler. - * - * This operation must fetch the latest thread priority value for this - * scheduler instance and update its internal state if necessary. - * - * @param[in] the_thread The thread. - * - * @see _Scheduler_Node_get_priority(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Unblock( Thread_Control *the_thread ) -{ - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - scheduler = _Thread_Scheduler_get_home( the_thread ); - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.unblock )( - scheduler, - the_thread, - _Thread_Scheduler_get_home_node( the_thread ) - ); - _Scheduler_Release_critical( scheduler, &lock_context ); -} - -/** - * @brief Propagates a priority change of a thread to the scheduler. - * - * On uni-processor configurations, this operation must evaluate the thread - * state. In case the thread is not ready, then the priority update should be - * deferred to the next scheduler unblock operation. - * - * The operation must update the heir and thread dispatch necessary variables - * in case the set of scheduled threads changes. - * - * @param[in] the_thread The thread changing its priority. - * - * @see _Scheduler_Node_get_priority(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Update_priority( Thread_Control *the_thread ) -{ -#if defined(RTEMS_SMP) - Chain_Node *node; - const Chain_Node *tail; - - _Thread_Scheduler_process_requests( the_thread ); - - node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes ); - tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes ); - - do { - Scheduler_Node *scheduler_node; - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); - scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); - - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.update_priority )( - scheduler, - the_thread, - scheduler_node - ); - _Scheduler_Release_critical( scheduler, &lock_context ); - - node = _Chain_Next( node ); - } while ( node != tail ); -#else - const Scheduler_Control *scheduler; - - scheduler = _Thread_Scheduler_get_home( the_thread ); - ( *scheduler->Operations.update_priority )( - scheduler, - the_thread, - _Thread_Scheduler_get_home_node( the_thread ) - ); -#endif -} - -#if defined(RTEMS_SMP) -/** - * @brief Changes the sticky level of the home scheduler node and propagates a - * priority change of a thread to the scheduler. - * - * @param[in] the_thread The thread changing its priority or sticky level. - * - * @see _Scheduler_Update_priority(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Priority_and_sticky_update( - Thread_Control *the_thread, - int sticky_level_change -) -{ - Chain_Node *node; - const Chain_Node *tail; - Scheduler_Node *scheduler_node; - const Scheduler_Control *scheduler; - ISR_lock_Context lock_context; - - _Thread_Scheduler_process_requests( the_thread ); - - node = _Chain_First( &the_thread->Scheduler.Scheduler_nodes ); - scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); - scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); - - _Scheduler_Acquire_critical( scheduler, &lock_context ); - - scheduler_node->sticky_level += sticky_level_change; - _Assert( scheduler_node->sticky_level >= 0 ); - - ( *scheduler->Operations.update_priority )( - scheduler, - the_thread, - scheduler_node - ); - - _Scheduler_Release_critical( scheduler, &lock_context ); - - tail = _Chain_Immutable_tail( &the_thread->Scheduler.Scheduler_nodes ); - node = _Chain_Next( node ); - - while ( node != tail ) { - scheduler_node = SCHEDULER_NODE_OF_THREAD_SCHEDULER_NODE( node ); - scheduler = _Scheduler_Node_get_scheduler( scheduler_node ); - - _Scheduler_Acquire_critical( scheduler, &lock_context ); - ( *scheduler->Operations.update_priority )( - scheduler, - the_thread, - scheduler_node - ); - _Scheduler_Release_critical( scheduler, &lock_context ); - - node = _Chain_Next( node ); - } -} -#endif - -/** - * @brief Maps a thread priority from the user domain to the scheduler domain. - * - * Let M be the maximum scheduler priority. The mapping must be bijective in - * the closed interval [0, M], e.g. _Scheduler_Unmap_priority( scheduler, - * _Scheduler_Map_priority( scheduler, p ) ) == p for all p in [0, M]. For - * other values the mapping is undefined. - * - * @param[in] scheduler The scheduler instance. - * @param[in] priority The user domain thread priority. - * - * @return The corresponding thread priority of the scheduler domain is returned. - */ -RTEMS_INLINE_ROUTINE Priority_Control _Scheduler_Map_priority( - const Scheduler_Control *scheduler, - Priority_Control priority -) -{ - return ( *scheduler->Operations.map_priority )( scheduler, priority ); -} - -/** - * @brief Unmaps a thread priority from the scheduler domain to the user domain. - * - * @param[in] scheduler The scheduler instance. - * @param[in] priority The scheduler domain thread priority. - * - * @return The corresponding thread priority of the user domain is returned. - */ -RTEMS_INLINE_ROUTINE Priority_Control _Scheduler_Unmap_priority( - const Scheduler_Control *scheduler, - Priority_Control priority -) -{ - return ( *scheduler->Operations.unmap_priority )( scheduler, priority ); -} - -/** - * @brief Initializes a scheduler node. - * - * The scheduler node contains arbitrary data on function entry. The caller - * must ensure that _Scheduler_Node_destroy() will be called after a - * _Scheduler_Node_initialize() before the memory of the scheduler node is - * destroyed. - * - * @param[in] scheduler The scheduler instance. - * @param[in] node The scheduler node to initialize. - * @param[in] the_thread The thread of the scheduler node to initialize. - * @param[in] priority The thread priority. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Node_initialize( - const Scheduler_Control *scheduler, - Scheduler_Node *node, - Thread_Control *the_thread, - Priority_Control priority -) -{ - ( *scheduler->Operations.node_initialize )( - scheduler, - node, - the_thread, - priority - ); -} - -/** - * @brief Destroys a scheduler node. - * - * The caller must ensure that _Scheduler_Node_destroy() will be called only - * after a corresponding _Scheduler_Node_initialize(). - * - * @param[in] scheduler The scheduler instance. - * @param[in] node The scheduler node to destroy. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Node_destroy( - const Scheduler_Control *scheduler, - Scheduler_Node *node -) -{ - ( *scheduler->Operations.node_destroy )( scheduler, node ); -} - -/** - * @brief Releases a job of a thread with respect to the scheduler. - * - * @param[in] the_thread The thread. - * @param[in] priority_node The priority node of the job. - * @param[in] deadline The deadline in watchdog ticks since boot. - * @param[in] queue_context The thread queue context to provide the set of - * threads for _Thread_Priority_update(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Release_job( - Thread_Control *the_thread, - Priority_Node *priority_node, - uint64_t deadline, - Thread_queue_Context *queue_context -) -{ - const Scheduler_Control *scheduler = _Thread_Scheduler_get_home( the_thread ); - - _Thread_queue_Context_clear_priority_updates( queue_context ); - ( *scheduler->Operations.release_job )( - scheduler, - the_thread, - priority_node, - deadline, - queue_context - ); -} - -/** - * @brief Cancels a job of a thread with respect to the scheduler. - * - * @param[in] the_thread The thread. - * @param[in] priority_node The priority node of the job. - * @param[in] queue_context The thread queue context to provide the set of - * threads for _Thread_Priority_update(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Cancel_job( - Thread_Control *the_thread, - Priority_Node *priority_node, - Thread_queue_Context *queue_context -) -{ - const Scheduler_Control *scheduler = _Thread_Scheduler_get_home( the_thread ); - - _Thread_queue_Context_clear_priority_updates( queue_context ); - ( *scheduler->Operations.cancel_job )( - scheduler, - the_thread, - priority_node, - queue_context - ); -} - -/** - * @brief Scheduler method invoked at each clock tick. - * - * This method is invoked at each clock tick to allow the scheduler - * implementation to perform any activities required. For the - * scheduler which support standard RTEMS features, this includes - * time-slicing management. - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Tick( const Per_CPU_Control *cpu ) -{ - const Scheduler_Control *scheduler = _Scheduler_Get_by_CPU( cpu ); - Thread_Control *executing = cpu->executing; - - if ( scheduler != NULL && executing != NULL ) { - ( *scheduler->Operations.tick )( scheduler, executing ); - } -} - -/** - * @brief Starts the idle thread for a particular processor. - * - * @param[in] scheduler The scheduler instance. - * @param[in,out] the_thread The idle thread for the processor. - * @param[in,out] cpu The processor for the idle thread. - * - * @see _Thread_Create_idle(). - */ -RTEMS_INLINE_ROUTINE void _Scheduler_Start_idle( - const Scheduler_Control *scheduler, - Thread_Control *the_thread, - Per_CPU_Control *cpu -) -{ - ( *scheduler->Operations.start_idle )( scheduler, the_thread, cpu ); -} - -RTEMS_INLINE_ROUTINE bool _Scheduler_Has_processor_ownership( - const Scheduler_Control *scheduler, - uint32_t cpu_index -) -{ -#if defined(RTEMS_SMP) - const Per_CPU_Control *cpu; - const Scheduler_Control *scheduler_of_cpu; - - cpu = _Per_CPU_Get_by_index( cpu_index ); - scheduler_of_cpu = _Scheduler_Get_by_CPU( cpu ); - - return scheduler_of_cpu == scheduler; -#else - (void) scheduler; - (void) cpu_index; - - return true; -#endif -} - -RTEMS_INLINE_ROUTINE const Processor_mask *_Scheduler_Get_processors( - const Scheduler_Control *scheduler -) -{ -#if defined(RTEMS_SMP) - return &_Scheduler_Get_context( scheduler )->Processors; -#else - return &_Processor_mask_The_one_and_only; -#endif -} - -bool _Scheduler_Get_affinity( - Thread_Control *the_thread, - size_t cpusetsize, - cpu_set_t *cpuset -); - -RTEMS_INLINE_ROUTINE bool _Scheduler_default_Set_affinity_body( - const Scheduler_Control *scheduler, - Thread_Control *the_thread, - Scheduler_Node *node, - const Processor_mask *affinity -) -{ - (void) scheduler; - (void) the_thread; - (void) node; - return _Processor_mask_Is_subset( affinity, _SMP_Get_online_processors() ); -} - -bool _Scheduler_Set_affinity( - Thread_Control *the_thread, - size_t cpusetsize, - const cpu_set_t *cpuset -); - -RTEMS_INLINE_ROUTINE void _Scheduler_Generic_block( - const Scheduler_Control *scheduler, - Thread_Control *the_thread, - Scheduler_Node *node, - void ( *extract )( - const Scheduler_Control *, - Thread_Control *, - Scheduler_Node * - ), - void ( *schedule )( - const Scheduler_Control *, - Thread_Control *, - bool - ) -) -{ - ( *extract )( scheduler, the_thread, node ); - - /* TODO: flash critical section? */ - - if ( _Thread_Is_executing( the_thread ) || _Thread_Is_heir( the_thread ) ) { - ( *schedule )( scheduler, the_thread, true ); - } -} - -RTEMS_INLINE_ROUTINE uint32_t _Scheduler_Get_processor_count( - const Scheduler_Control *scheduler -) -{ -#if defined(RTEMS_SMP) - const Scheduler_Context *context = _Scheduler_Get_context( scheduler ); - - return _Processor_mask_Count( &context->Processors ); -#else - (void) scheduler; - - return 1; -#endif -} - -RTEMS_INLINE_ROUTINE Objects_Id _Scheduler_Build_id( uint32_t scheduler_index ) -{ - return _Objects_Build_id( - OBJECTS_FAKE_OBJECTS_API, - OBJECTS_FAKE_OBJECTS_SCHEDULERS, - _Objects_Local_node, - (uint16_t) ( scheduler_index + 1 ) - ); -} - -RTEMS_INLINE_ROUTINE uint32_t _Scheduler_Get_index_by_id( Objects_Id id ) -{ - uint32_t minimum_id = _Scheduler_Build_id( 0 ); - - return id - minimum_id; -} - -RTEMS_INLINE_ROUTINE const Scheduler_Control *_Scheduler_Get_by_id( - Objects_Id id -) -{ - uint32_t index; - - index = _Scheduler_Get_index_by_id( id ); - - if ( index >= _Scheduler_Count ) { - return NULL; - } - - return &_Scheduler_Table[ index ]; -} - -RTEMS_INLINE_ROUTINE uint32_t _Scheduler_Get_index( - const Scheduler_Control *scheduler -) -{ - return (uint32_t) (scheduler - &_Scheduler_Table[ 0 ]); -} - -#if defined(RTEMS_SMP) -/** - * @brief Gets an idle thread from the scheduler instance. - * - * @param[in] context The scheduler instance context. - * - * @retval idle An idle thread for use. This function must always return an - * idle thread. If none is available, then this is a fatal error. - */ -typedef Thread_Control *( *Scheduler_Get_idle_thread )( - Scheduler_Context *context -); - -/** - * @brief Releases an idle thread to the scheduler instance for reuse. - * - * @param[in] context The scheduler instance context. - * @param[in] idle The idle thread to release - */ -typedef void ( *Scheduler_Release_idle_thread )( - Scheduler_Context *context, - Thread_Control *idle -); - -RTEMS_INLINE_ROUTINE void _Scheduler_Thread_change_state( - Thread_Control *the_thread, - Thread_Scheduler_state new_state -) -{ - _Assert( - _ISR_lock_Is_owner( &the_thread->Scheduler.Lock ) - || the_thread->Scheduler.state == THREAD_SCHEDULER_BLOCKED - || !_System_state_Is_up( _System_state_Get() ) - ); - - the_thread->Scheduler.state = new_state; -} - -RTEMS_INLINE_ROUTINE void _Scheduler_Set_idle_thread( - Scheduler_Node *node, - Thread_Control *idle -) -{ - _Assert( _Scheduler_Node_get_idle( node ) == NULL ); - _Assert( - _Scheduler_Node_get_owner( node ) == _Scheduler_Node_get_user( node ) - ); - - _Scheduler_Node_set_user( node, idle ); - node->idle = idle; -} - -/** - * @brief Use an idle thread for this scheduler node. - * - * A thread those home scheduler node has a sticky level greater than zero may - * use an idle thread in the home scheduler instance in case it executes - * currently in another scheduler instance or in case it is in a blocking - * state. - * - * @param[in] context The scheduler instance context. - * @param[in] node The node which wants to use the idle thread. - * @param[in] cpu The processor for the idle thread. - * @param[in] get_idle_thread Function to get an idle thread. - */ -RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Use_idle_thread( - Scheduler_Context *context, - Scheduler_Node *node, - Per_CPU_Control *cpu, - Scheduler_Get_idle_thread get_idle_thread -) -{ - Thread_Control *idle = ( *get_idle_thread )( context ); - - _Scheduler_Set_idle_thread( node, idle ); - _Thread_Set_CPU( idle, cpu ); - return idle; -} - -typedef enum { - SCHEDULER_TRY_TO_SCHEDULE_DO_SCHEDULE, - SCHEDULER_TRY_TO_SCHEDULE_DO_IDLE_EXCHANGE, - SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK -} Scheduler_Try_to_schedule_action; - -/** - * @brief Try to schedule this scheduler node. - * - * @param[in] context The scheduler instance context. - * @param[in] node The node which wants to get scheduled. - * @param[in] idle A potential idle thread used by a potential victim node. - * @param[in] get_idle_thread Function to get an idle thread. - * - * @retval true This node can be scheduled. - * @retval false Otherwise. - */ -RTEMS_INLINE_ROUTINE Scheduler_Try_to_schedule_action -_Scheduler_Try_to_schedule_node( - Scheduler_Context *context, - Scheduler_Node *node, - Thread_Control *idle, - Scheduler_Get_idle_thread get_idle_thread -) -{ - ISR_lock_Context lock_context; - Scheduler_Try_to_schedule_action action; - Thread_Control *owner; - - action = SCHEDULER_TRY_TO_SCHEDULE_DO_SCHEDULE; - owner = _Scheduler_Node_get_owner( node ); - _Assert( _Scheduler_Node_get_user( node ) == owner ); - _Assert( _Scheduler_Node_get_idle( node ) == NULL ); - - _Thread_Scheduler_acquire_critical( owner, &lock_context ); - - if ( owner->Scheduler.state == THREAD_SCHEDULER_READY ) { - _Thread_Scheduler_cancel_need_for_help( owner, _Thread_Get_CPU( owner ) ); - _Scheduler_Thread_change_state( owner, THREAD_SCHEDULER_SCHEDULED ); - } else if ( - owner->Scheduler.state == THREAD_SCHEDULER_SCHEDULED - && node->sticky_level <= 1 - ) { - action = SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK; - } else if ( node->sticky_level == 0 ) { - action = SCHEDULER_TRY_TO_SCHEDULE_DO_BLOCK; - } else if ( idle != NULL ) { - action = SCHEDULER_TRY_TO_SCHEDULE_DO_IDLE_EXCHANGE; - } else { - _Scheduler_Use_idle_thread( - context, - node, - _Thread_Get_CPU( owner ), - get_idle_thread - ); - } - - _Thread_Scheduler_release_critical( owner, &lock_context ); - return action; -} - -/** - * @brief Release an idle thread using this scheduler node. - * - * @param[in] context The scheduler instance context. - * @param[in] node The node which may have an idle thread as user. - * @param[in] release_idle_thread Function to release an idle thread. - * - * @retval idle The idle thread which used this node. - * @retval NULL This node had no idle thread as an user. - */ -RTEMS_INLINE_ROUTINE Thread_Control *_Scheduler_Release_idle_thread( - Scheduler_Context *context, - Scheduler_Node *node, - Scheduler_Release_idle_thread release_idle_thread -) -{ - Thread_Control *idle = _Scheduler_Node_get_idle( node ); - - if ( idle != NULL ) { - Thread_Control *owner = _Scheduler_Node_get_owner( node ); - - node->idle = NULL; - _Scheduler_Node_set_user( node, owner ); - ( *release_idle_thread )( context, idle ); - } - - return idle; -} - -RTEMS_INLINE_ROUTINE void _Scheduler_Exchange_idle_thread( - Scheduler_Node *needs_idle, - Scheduler_Node *uses_idle, - Thread_Control *idle -) -{ - uses_idle->idle = NULL; - _Scheduler_Node_set_user( - uses_idle, - _Scheduler_Node_get_owner( uses_idle ) - ); - _Scheduler_Set_idle_thread( needs_idle, idle ); -} - -/** - * @brief Block this scheduler node. - * - * @param[in] context The scheduler instance context. - * @param[in] thread The thread which wants to get blocked referencing this - * node. This is not necessarily the user of this node in case the node - * participates in the scheduler helping protocol. - * @param[in] node The node which wants to get blocked. - * @param[in] is_scheduled This node is scheduled. - * @param[in] get_idle_thread Function to get an idle thread. - * - * @retval thread_cpu The processor of the thread. Indicates to continue with - * the blocking operation. - * @retval NULL Otherwise. - */ -RTEMS_INLINE_ROUTINE Per_CPU_Control *_Scheduler_Block_node( - Scheduler_Context *context, - Thread_Control *thread, - Scheduler_Node *node, - bool is_scheduled, - Scheduler_Get_idle_thread get_idle_thread -) -{ - int sticky_level; - ISR_lock_Context lock_context; - Per_CPU_Control *thread_cpu; - - sticky_level = node->sticky_level; - --sticky_level; - node->sticky_level = sticky_level; - _Assert( sticky_level >= 0 ); - - _Thread_Scheduler_acquire_critical( thread, &lock_context ); - thread_cpu = _Thread_Get_CPU( thread ); - _Thread_Scheduler_cancel_need_for_help( thread, thread_cpu ); - _Scheduler_Thread_change_state( thread, THREAD_SCHEDULER_BLOCKED ); - _Thread_Scheduler_release_critical( thread, &lock_context ); - - if ( sticky_level > 0 ) { - if ( is_scheduled && _Scheduler_Node_get_idle( node ) == NULL ) { - Thread_Control *idle; - - idle = _Scheduler_Use_idle_thread( - context, - node, - thread_cpu, - get_idle_thread - ); - _Thread_Dispatch_update_heir( _Per_CPU_Get(), thread_cpu, idle ); - } - - return NULL; - } - - _Assert( thread == _Scheduler_Node_get_user( node ) ); - return thread_cpu; -} - -RTEMS_INLINE_ROUTINE void _Scheduler_Discard_idle_thread( - Scheduler_Context *context, - Thread_Control *the_thread, - Scheduler_Node *node, - Scheduler_Release_idle_thread release_idle_thread -) -{ - Thread_Control *idle; - Thread_Control *owner; - Per_CPU_Control *cpu; - - idle = _Scheduler_Node_get_idle( node ); - owner = _Scheduler_Node_get_owner( node ); - - node->idle = NULL; - _Assert( _Scheduler_Node_get_user( node ) == idle ); - _Scheduler_Node_set_user( node, owner ); - ( *release_idle_thread )( context, idle ); - - cpu = _Thread_Get_CPU( idle ); - _Thread_Set_CPU( the_thread, cpu ); - _Thread_Dispatch_update_heir( _Per_CPU_Get(), cpu, the_thread ); -} - -/** - * @brief Unblock this scheduler node. - * - * @param[in] context The scheduler instance context. - * @param[in] the_thread The thread which wants to get unblocked. - * @param[in] node The node which wants to get unblocked. - * @param[in] is_scheduled This node is scheduled. - * @param[in] release_idle_thread Function to release an idle thread. - * - * @retval true Continue with the unblocking operation. - * @retval false Otherwise. - */ -RTEMS_INLINE_ROUTINE bool _Scheduler_Unblock_node( - Scheduler_Context *context, - Thread_Control *the_thread, - Scheduler_Node *node, - bool is_scheduled, - Scheduler_Release_idle_thread release_idle_thread -) -{ - bool unblock; - - ++node->sticky_level; - _Assert( node->sticky_level > 0 ); - - if ( is_scheduled ) { - _Scheduler_Discard_idle_thread( - context, - the_thread, - node, - release_idle_thread - ); - _Scheduler_Thread_change_state( the_thread, THREAD_SCHEDULER_SCHEDULED ); - unblock = false; - } else { - _Scheduler_Thread_change_state( the_thread, THREAD_SCHEDULER_READY ); - unblock = true; - } - - return unblock; -} -#endif - -RTEMS_INLINE_ROUTINE void _Scheduler_Update_heir( - Thread_Control *new_heir, - bool force_dispatch -) -{ - Thread_Control *heir = _Thread_Heir; - - if ( heir != new_heir && ( heir->is_preemptible || force_dispatch ) ) { -#if defined(RTEMS_SMP) - /* - * We need this state only for _Thread_Get_CPU_time_used(). Cannot use - * _Scheduler_Thread_change_state() since THREAD_SCHEDULER_BLOCKED to - * THREAD_SCHEDULER_BLOCKED state changes are illegal for the real SMP - * schedulers. - */ - heir->Scheduler.state = THREAD_SCHEDULER_BLOCKED; - new_heir->Scheduler.state = THREAD_SCHEDULER_SCHEDULED; -#endif - _Thread_Update_CPU_time_used( heir, _Thread_Get_CPU( heir ) ); - _Thread_Heir = new_heir; - _Thread_Dispatch_necessary = true; - } -} - -RTEMS_INLINE_ROUTINE Status_Control _Scheduler_Set( - const Scheduler_Control *new_scheduler, - Thread_Control *the_thread, - Priority_Control priority -) -{ - Scheduler_Node *new_scheduler_node; - Scheduler_Node *old_scheduler_node; -#if defined(RTEMS_SMP) - ISR_lock_Context lock_context; - const Scheduler_Control *old_scheduler; - -#endif - - if ( the_thread->Wait.queue != NULL ) { - return STATUS_RESOURCE_IN_USE; - } - - old_scheduler_node = _Thread_Scheduler_get_home_node( the_thread ); - _Priority_Plain_extract( - &old_scheduler_node->Wait.Priority, - &the_thread->Real_priority - ); - - if ( !_Priority_Is_empty( &old_scheduler_node->Wait.Priority ) ) { - _Priority_Plain_insert( - &old_scheduler_node->Wait.Priority, - &the_thread->Real_priority, - the_thread->Real_priority.priority - ); - return STATUS_RESOURCE_IN_USE; - } - -#if defined(RTEMS_SMP) - if ( !_Chain_Has_only_one_node( &the_thread->Scheduler.Wait_nodes ) ) { - _Priority_Plain_insert( - &old_scheduler_node->Wait.Priority, - &the_thread->Real_priority, - the_thread->Real_priority.priority - ); - return STATUS_RESOURCE_IN_USE; - } - - old_scheduler = _Thread_Scheduler_get_home( the_thread ); - new_scheduler_node = _Thread_Scheduler_get_node_by_index( - the_thread, - _Scheduler_Get_index( new_scheduler ) - ); - - _Scheduler_Acquire_critical( new_scheduler, &lock_context ); - - if ( - _Scheduler_Get_processor_count( new_scheduler ) == 0 - || !( *new_scheduler->Operations.set_affinity )( - new_scheduler, - the_thread, - new_scheduler_node, - &the_thread->Scheduler.Affinity - ) - ) { - _Scheduler_Release_critical( new_scheduler, &lock_context ); - _Priority_Plain_insert( - &old_scheduler_node->Wait.Priority, - &the_thread->Real_priority, - the_thread->Real_priority.priority - ); - return STATUS_UNSATISFIED; - } - - the_thread->Scheduler.home = new_scheduler; - - _Scheduler_Release_critical( new_scheduler, &lock_context ); - - _Thread_Scheduler_process_requests( the_thread ); -#else - new_scheduler_node = old_scheduler_node; -#endif - - the_thread->Start.initial_priority = priority; - _Priority_Node_set_priority( &the_thread->Real_priority, priority ); - _Priority_Initialize_one( - &new_scheduler_node->Wait.Priority, - &the_thread->Real_priority - ); - -#if defined(RTEMS_SMP) - if ( old_scheduler != new_scheduler ) { - States_Control current_state; - - current_state = the_thread->current_state; - - if ( _States_Is_ready( current_state ) ) { - _Scheduler_Block( the_thread ); - } - - _Assert( old_scheduler_node->sticky_level == 0 ); - _Assert( new_scheduler_node->sticky_level == 0 ); - - _Chain_Extract_unprotected( &old_scheduler_node->Thread.Wait_node ); - _Assert( _Chain_Is_empty( &the_thread->Scheduler.Wait_nodes ) ); - _Chain_Initialize_one( - &the_thread->Scheduler.Wait_nodes, - &new_scheduler_node->Thread.Wait_node - ); - _Chain_Extract_unprotected( - &old_scheduler_node->Thread.Scheduler_node.Chain - ); - _Assert( _Chain_Is_empty( &the_thread->Scheduler.Scheduler_nodes ) ); - _Chain_Initialize_one( - &the_thread->Scheduler.Scheduler_nodes, - &new_scheduler_node->Thread.Scheduler_node.Chain - ); - - _Scheduler_Node_set_priority( new_scheduler_node, priority, false ); - - if ( _States_Is_ready( current_state ) ) { - _Scheduler_Unblock( the_thread ); - } - - return STATUS_SUCCESSFUL; - } -#endif - - _Scheduler_Node_set_priority( new_scheduler_node, priority, false ); - _Scheduler_Update_priority( the_thread ); - return STATUS_SUCCESSFUL; -} - -/** @} */ - -#ifdef __cplusplus -} -#endif - -#endif -/* end of include file */ |