summaryrefslogblamecommitdiffstats
path: root/cpukit/score/src/schedulerstrongapa.c
blob: b38a0f96f0d659e07f994916460629b4dab37b43 (plain) (tree)
1
2
3
4
5
6
7
8
9

                                           


        
                                        
  

                                                         
                                                
                                                                         














                                                                              

                                                     




                                                                               


                                                                               


   
                                 
                                                         
  







                                                                          
  










                                                                              

   

                    


                                           
                                         
                               
 










                                                                              



                                                  
                                         




                                                           
                                                   
                             

                                 

 




                                                                

 





                                                           

 
                                     

                                     
                                     

                                                   






































                                                                               
                        




                                          

                                                                    
 
                                                                   



                                          
       
    











                                                                        
                                                    






































































                                                                                 





                                                              


                       
























































                                                                               







                                                                       
                                                                 



                                  


    
                                                      
                             

                                    

 

                                     
 


                                                          





                                                                 
   

 
                                                                      
                             
                                       

 











                                                                         


    






                                                                               
                             
                           

 















































                                                                       

                                     




                                                      




                                                                   




                                    
                              


















                                                                                 

 






                                                                         
 




































                                                                               

 



                                                                
 

                                     
 




                                                                              

 


                                                            

 
                                     
 





                                                                
 

 





                                                                         
 
                                                       

















                                                
 































                                                                                 




                                                                              

                          

 
                                                    
                             



                                      

 










                                                     
 
 


                                                   

                                                                     













































                                                                            



                                                         






                                                                        

                                     

                                                                        




                                                                   




                                             
                       




















                                                                                    

 








                                                                    

 



                                          
                                                      

                                                 
 

































                                                                        





                                                                        








                                          
                                                           




                                    
                                   
            
         

                                       

                                             

                                       
                                                       


                                             


    
                                                         
                             

                                

 
                                     
            
               
         


                                       
                                                       
                                               

                                             


    
                                                           
                             

                               

 























                                                                           
            
           
         
                                                 
                                             

                                           


    
                                 
                                     
                                  
                               



                                                                   








                                                                              
            
           
         



                                                       
                                             
                                  


    



                                     

 


                                                                   
            
           
         
                                    

                                      


    
                                           
                                     
                                  
                               



                                                                   
                                 
            
           
         
                                                 

                                             

                                            











                                                                   




                                               































                                                                   
                                                 


                                                       
                                             
                                  


    




























                                                       


                                             


    











                                                              










                                                                   
                                            


















                                                












                                                                   
                                                 
                                             


                                      


    
                                           
                                     
                                
                                      
                                   

 

                                         
 







                                                                              

    































                                                                         
                                                



                                                      


                                            



                           
/* SPDX-License-Identifier: BSD-2-Clause */

/**
 * @file
 *
 * @ingroup RTEMSScoreSchedulerStrongAPA
 *
 * @brief This source file contains the implementation of
 *   _Scheduler_strong_APA_Add_processor(),
 *   _Scheduler_strong_APA_Allocate_processor(),
 *   _Scheduler_strong_APA_Ask_for_help(), _Scheduler_strong_APA_Block(),
 *   _Scheduler_strong_APA_Do_ask_for_help(),
 *   _Scheduler_strong_APA_Do_enqueue(),
 *   _Scheduler_strong_APA_Do_set_affinity(),
 *   _Scheduler_strong_APA_Do_update(), _Scheduler_strong_APA_Enqueue(),
 *   _Scheduler_strong_APA_Enqueue_scheduled(),
 *   _Scheduler_strong_APA_Extract_from_ready(),
 *   _Scheduler_strong_APA_Extract_from_scheduled(),
 *   _Scheduler_strong_APA_Find_highest_ready(),
 *   _Scheduler_strong_APA_Get_highest_ready(),
 *   _Scheduler_strong_APA_Get_lowest_reachable(),
 *   _Scheduler_strong_APA_Get_lowest_scheduled(),
 *   _Scheduler_strong_APA_Has_ready(),
 *   _Scheduler_strong_APA_Initialize(), _Scheduler_strong_APA_Insert_ready(),
 *   _Scheduler_strong_APA_Move_from_ready_to_scheduled(),
 *   _Scheduler_strong_APA_Move_from_scheduled_to_ready(),
 *   _Scheduler_strong_APA_Node_initialize(),
 *   _Scheduler_strong_APA_Reconsider_help_request(),
 *   _Scheduler_strong_APA_Register_idle(),
 *   _Scheduler_strong_APA_Remove_processor(),
 *   _Scheduler_strong_APA_Set_affinity(),
 *   _Scheduler_strong_APA_Set_scheduled(), _Scheduler_strong_APA_Start_idle(),
 *   _Scheduler_strong_APA_Unblock(), _Scheduler_strong_APA_Update_priority(),
 *   _Scheduler_strong_APA_Withdraw_node(),
 *   _Scheduler_strong_APA_Make_sticky(), _Scheduler_strong_APA_Clean_sticky(),
 *   and _Scheduler_strong_APA_Yield().
 */

/*
 * Copyright (C) 2020 Richi Dubey
 * Copyright (C) 2013, 2016 embedded brains GmbH & Co. KG
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <rtems/score/schedulerstrongapa.h>
#include <rtems/score/schedulersmpimpl.h>
#include <rtems/score/assert.h>

#define STRONG_SCHEDULER_NODE_OF_CHAIN( node ) \
  RTEMS_CONTAINER_OF( node, Scheduler_strong_APA_Node, Ready_node )

static inline Scheduler_strong_APA_Context *
_Scheduler_strong_APA_Get_context( const Scheduler_Control *scheduler )
{
  return (Scheduler_strong_APA_Context *) _Scheduler_Get_context( scheduler );
}

static inline Scheduler_strong_APA_Context *
_Scheduler_strong_APA_Get_self( Scheduler_Context *context )
{
  return (Scheduler_strong_APA_Context *) context;
}

static inline Scheduler_strong_APA_Node *
_Scheduler_strong_APA_Node_downcast( Scheduler_Node *node )
{
  return (Scheduler_strong_APA_Node *) node;
}

static inline void _Scheduler_strong_APA_Do_update(
  Scheduler_Context *context,
  Scheduler_Node    *node,
  Priority_Control   new_priority
)
{
  Scheduler_SMP_Node *smp_node;
  (void) context;

  smp_node = _Scheduler_SMP_Node_downcast( node );
  _Scheduler_SMP_Node_update_priority( smp_node, new_priority );
}

/*
 * Returns true if the Strong APA scheduler has ready nodes
 * available for scheduling.
 */
static inline bool _Scheduler_strong_APA_Has_ready(
  Scheduler_Context *context
)
{
  Scheduler_strong_APA_Context *self;
  const Chain_Node             *tail;
  Chain_Node                   *next;
  Scheduler_strong_APA_Node    *node;

  self = _Scheduler_strong_APA_Get_self( context );
  tail = _Chain_Immutable_tail( &self->Ready );
  next = _Chain_First( &self->Ready );

  while ( next != tail ) {
    node = (Scheduler_strong_APA_Node *)STRONG_SCHEDULER_NODE_OF_CHAIN( next );

    if (
      _Scheduler_SMP_Node_state( &node->Base.Base ) ==
      SCHEDULER_SMP_NODE_READY
    ) {
      return true;
    }

    next = _Chain_Next( next );
  }

  return false;
}

static inline void _Scheduler_strong_APA_Set_scheduled(
  Scheduler_strong_APA_Context *self,
  Scheduler_Node                *executing,
  const Per_CPU_Control         *cpu
)
{
  self->CPU[ _Per_CPU_Get_index( cpu ) ].executing = executing;
}

static inline Scheduler_Node *_Scheduler_strong_APA_Get_scheduled(
  const Scheduler_strong_APA_Context *self,
  const Per_CPU_Control               *cpu
)
{
  return self->CPU[ _Per_CPU_Get_index( cpu ) ].executing;
}

static inline void _Scheduler_strong_APA_Allocate_processor(
  Scheduler_Context *context,
  Scheduler_Node    *scheduled_base,
  Per_CPU_Control   *cpu
)
{
  Scheduler_strong_APA_Node    *scheduled;
  Scheduler_strong_APA_Context *self;

  scheduled = _Scheduler_strong_APA_Node_downcast( scheduled_base );
  self = _Scheduler_strong_APA_Get_self( context );

  _Scheduler_strong_APA_Set_scheduled( self, scheduled_base, cpu );

  _Scheduler_SMP_Allocate_processor_exact(
    context,
    &( scheduled->Base.Base ),
    cpu
  );
}

/*
 * Finds and returns the highest ready node present by accessing the
 * _Strong_APA_Context->CPU with front and rear values.
 */
static inline Scheduler_Node * _Scheduler_strong_APA_Find_highest_ready(
  Scheduler_strong_APA_Context *self,
  uint32_t                      front,
  uint32_t                      rear
)
{
  Scheduler_Node              *highest_ready = NULL;
  Scheduler_strong_APA_CPU    *CPU;
  const Chain_Node            *tail;
  Chain_Node                  *next;
  Scheduler_strong_APA_Node   *node;
  Priority_Control             min_priority_num;
  Priority_Control             curr_priority;
  Per_CPU_Control             *assigned_cpu;
  Scheduler_SMP_Node_state     curr_state;
  Per_CPU_Control             *curr_CPU;

  CPU = self->CPU;
  /*
   * When the first task accessed has nothing to compare its priority against.
   * So, it is the task with the highest priority witnessed so far.
   */
  min_priority_num = UINT64_MAX;

  while ( front <= rear ) {
    curr_CPU = CPU[ front++ ].cpu;

    tail = _Chain_Immutable_tail( &self->Ready );
    next = _Chain_First( &self->Ready );

    while ( next != tail ) {
      node = (Scheduler_strong_APA_Node*) STRONG_SCHEDULER_NODE_OF_CHAIN( next );
      /*
       * Check if the curr_CPU is in the affinity set of the node.
       */
      if (
        _Processor_mask_Is_set( &node->Affinity, _Per_CPU_Get_index( curr_CPU ) )
      ) {
        curr_state = _Scheduler_SMP_Node_state( &node->Base.Base );

        if ( curr_state == SCHEDULER_SMP_NODE_SCHEDULED ) {
          assigned_cpu = _Thread_Get_CPU( node->Base.Base.user );

          if ( CPU[ _Per_CPU_Get_index( assigned_cpu ) ].visited == false ) {
            CPU[ ++rear ].cpu = assigned_cpu;
            CPU[ _Per_CPU_Get_index( assigned_cpu ) ].visited = true;
            /*
             * The curr CPU of the queue invoked this node to add its CPU
             * that it is executing on to the queue. So this node might get
             * preempted because of the invoker curr_CPU and this curr_CPU
             * is the CPU that node should preempt in case this node
             * gets preempted.
             */
            node->cpu_to_preempt = curr_CPU;
          }
        } else if ( curr_state == SCHEDULER_SMP_NODE_READY ) {
          curr_priority = _Scheduler_Node_get_priority( &node->Base.Base );
          curr_priority = SCHEDULER_PRIORITY_PURIFY( curr_priority );

          if (
            min_priority_num == UINT64_MAX ||
            curr_priority < min_priority_num
          ) {
            min_priority_num = curr_priority;
            highest_ready = &node->Base.Base;
            /*
             * In case curr_CPU is filter_CPU, we need to store the
             * cpu_to_preempt value so that we go back to SMP_*
             * function, rather than preempting the node ourselves.
             */
            node->cpu_to_preempt = curr_CPU;
          }
        }
      }
    next = _Chain_Next( next );
    }
  }

  /*
   * By definition, the system would always have a ready node,
   * hence highest_ready would not be NULL.
   */
  _Assert( highest_ready != NULL );

  return highest_ready;
}

static inline Scheduler_Node *_Scheduler_strong_APA_Get_idle( void *arg )
{
  Scheduler_strong_APA_Context *self;
  Scheduler_strong_APA_Node    *lowest_ready = NULL;
  Priority_Control              max_priority_num;
  const Chain_Node             *tail;
  Chain_Node                   *next;

  self = _Scheduler_strong_APA_Get_self( arg );
  tail = _Chain_Immutable_tail( &self->Ready );
  next = _Chain_First( &self->Ready );
  max_priority_num = 0;

  while ( next != tail ) {
    Scheduler_strong_APA_Node *node;
    Scheduler_SMP_Node_state   curr_state;

    node = (Scheduler_strong_APA_Node*) STRONG_SCHEDULER_NODE_OF_CHAIN( next );
    curr_state = _Scheduler_SMP_Node_state( &node->Base.Base );

    if ( curr_state == SCHEDULER_SMP_NODE_READY ) {
      Priority_Control curr_priority;

      curr_priority = _Scheduler_Node_get_priority( &node->Base.Base );

      if ( curr_priority > max_priority_num ) {
        max_priority_num = curr_priority;
        lowest_ready = node;
      }
    }

    next = _Chain_Next( next );
  }

  _Assert( lowest_ready != NULL );
  _Chain_Extract_unprotected( &lowest_ready->Ready_node );
  _Chain_Set_off_chain( &lowest_ready->Ready_node );

  return &lowest_ready->Base.Base;
}

static inline void _Scheduler_strong_APA_Release_idle(
  Scheduler_Node *node_base,
  void           *arg
)
{
  Scheduler_strong_APA_Context *self;
  Scheduler_strong_APA_Node    *node;

  self = _Scheduler_strong_APA_Get_self( arg );
  node = _Scheduler_strong_APA_Node_downcast( node_base );

  if ( _Chain_Is_node_off_chain( &node->Ready_node ) ) {
    _Chain_Append_unprotected( &self->Ready, &node->Ready_node );
  }
}

static inline void  _Scheduler_strong_APA_Move_from_ready_to_scheduled(
  Scheduler_Context *context,
  Scheduler_Node    *ready_to_scheduled
)
{
  Priority_Control insert_priority;

  insert_priority = _Scheduler_SMP_Node_priority( ready_to_scheduled );
  insert_priority = SCHEDULER_PRIORITY_APPEND( insert_priority );
  _Scheduler_SMP_Insert_scheduled(
    context,
    ready_to_scheduled,
    insert_priority
  );
}

static inline void _Scheduler_strong_APA_Insert_ready(
  Scheduler_Context *context,
  Scheduler_Node    *node_base,
  Priority_Control   insert_priority
)
{
  Scheduler_strong_APA_Context *self;
  Scheduler_strong_APA_Node    *node;

  self = _Scheduler_strong_APA_Get_self( context );
  node = _Scheduler_strong_APA_Node_downcast( node_base );

  if( _Chain_Is_node_off_chain( &node->Ready_node ) ) {
    _Chain_Append_unprotected( &self->Ready, &node->Ready_node );
  }  else {
    _Chain_Extract_unprotected( &node->Ready_node );
    _Chain_Set_off_chain( &node->Ready_node );
    _Chain_Append_unprotected( &self->Ready, &node->Ready_node );
  }
}

static inline void _Scheduler_strong_APA_Move_from_scheduled_to_ready(
  Scheduler_Context *context,
  Scheduler_Node    *scheduled_to_ready
)
{
  Priority_Control insert_priority;

  if( !_Chain_Is_node_off_chain(  &scheduled_to_ready->Node.Chain ) ) {
    _Scheduler_SMP_Extract_from_scheduled( context, scheduled_to_ready );
  }

  insert_priority = _Scheduler_SMP_Node_priority( scheduled_to_ready );

  _Scheduler_strong_APA_Insert_ready(
    context,
    scheduled_to_ready,
    insert_priority
  );
}

/*
 * Implement the BFS Algorithm for task departure to get the highest ready task
 * for a particular CPU, returns the highest ready Scheduler_Node
 * Scheduler_Node filter here points to the victim node that is blocked
 * resulting which this function is called.
 */
static inline Scheduler_Node *_Scheduler_strong_APA_Get_highest_ready(
  Scheduler_Context *context,
  Scheduler_Node    *filter
)
{
  Scheduler_strong_APA_Context *self;
  Per_CPU_Control              *filter_cpu;
  Scheduler_strong_APA_Node    *node;
  Scheduler_Node               *highest_ready;
  Scheduler_Node               *curr_node;
  Scheduler_Node               *next_node;
  Scheduler_strong_APA_CPU     *CPU;
  uint32_t                      front;
  uint32_t                      rear;
  uint32_t                      cpu_max;
  uint32_t                      cpu_index;

  self = _Scheduler_strong_APA_Get_self( context );
  /*
   * Denotes front and rear of the queue
   */
  front = 0;
  rear = -1;

  filter_cpu = _Thread_Get_CPU( filter->user );
  CPU = self->CPU;
  cpu_max = _SMP_Get_processor_maximum();

  for ( cpu_index = 0 ; cpu_index < cpu_max ; ++cpu_index ) {
    CPU[ cpu_index ].visited = false;
  }

  CPU[ ++rear ].cpu = filter_cpu;
  CPU[ _Per_CPU_Get_index( filter_cpu ) ].visited = true;

  highest_ready = _Scheduler_strong_APA_Find_highest_ready(
                    self,
                    front,
                    rear
                  );

  if ( highest_ready != filter ) {
    /*
     * Backtrack on the path from
     * filter_cpu to highest_ready, shifting along every task.
     */

    node = _Scheduler_strong_APA_Node_downcast( highest_ready );
    /*
     * Highest ready is not just directly reachable from the victim cpu
     * So there is need for task shifting.
     */
    while ( node->cpu_to_preempt != filter_cpu ) {
      Thread_Control *next_node_idle;

      curr_node = &node->Base.Base;
      next_node = _Scheduler_strong_APA_Get_scheduled(
                    self,
                    node->cpu_to_preempt
                  );
      next_node_idle = _Scheduler_Release_idle_thread_if_necessary(
        next_node,
        _Scheduler_strong_APA_Release_idle,
        context
      );

      (void) _Scheduler_SMP_Preempt(
               context,
               curr_node,
               next_node,
               next_node_idle,
               _Scheduler_strong_APA_Allocate_processor
             );

      if ( curr_node == highest_ready ) {
        _Scheduler_strong_APA_Move_from_ready_to_scheduled( context, curr_node );
      }

      node = _Scheduler_strong_APA_Node_downcast( next_node );
    }
    /*
     * To save the last node so that the caller SMP_* function
     * can do the allocation
     */
      curr_node = &node->Base.Base;
      highest_ready = curr_node;
      _Scheduler_strong_APA_Move_from_scheduled_to_ready( context, curr_node );
    }

  return highest_ready;
}

/*
 * Checks the lowest scheduled directly reachable task
 */
static inline Scheduler_Node *_Scheduler_strong_APA_Get_lowest_scheduled(
  Scheduler_Context *context,
  Scheduler_Node    *filter_base
)
{
  uint32_t	                cpu_max;
  uint32_t	                cpu_index;
  Scheduler_Node               *curr_node;
  Scheduler_Node               *lowest_scheduled = NULL;
  Priority_Control              max_priority_num;
  Priority_Control              curr_priority;
  Scheduler_strong_APA_Node    *filter_strong_node;
  Scheduler_strong_APA_Context *self;

  self = _Scheduler_strong_APA_Get_self( context );
  max_priority_num = 0;    /* Max (Lowest) priority encountered so far */
  filter_strong_node = _Scheduler_strong_APA_Node_downcast( filter_base );

  /* lowest_scheduled is NULL if affinity of a node is 0 */
  _Assert( !_Processor_mask_Is_zero( &filter_strong_node->Affinity ) );
  cpu_max = _SMP_Get_processor_maximum();

  for ( cpu_index = 0 ; cpu_index < cpu_max ; ++cpu_index ) {
    /* Checks if the CPU is in the affinity set of filter_strong_node */
    if ( _Processor_mask_Is_set( &filter_strong_node->Affinity, cpu_index ) ) {
      Per_CPU_Control *cpu = _Per_CPU_Get_by_index( cpu_index );

      if ( _Per_CPU_Is_processor_online( cpu ) ) {
        curr_node = _Scheduler_strong_APA_Get_scheduled( self, cpu );
        curr_priority = _Scheduler_Node_get_priority( curr_node );
        curr_priority = SCHEDULER_PRIORITY_PURIFY( curr_priority );

        if ( curr_priority > max_priority_num ) {
          lowest_scheduled = curr_node;
          max_priority_num = curr_priority;
        }
      }
    }
  }

  _Assert( lowest_scheduled != NULL );
  return lowest_scheduled;
}

static inline void _Scheduler_strong_APA_Extract_from_scheduled(
  Scheduler_Context *context,
  Scheduler_Node    *node_to_extract
)
{
  Scheduler_strong_APA_Context *self;
  Scheduler_strong_APA_Node    *node;

  self = _Scheduler_strong_APA_Get_self( context );
  node = _Scheduler_strong_APA_Node_downcast( node_to_extract );

  _Scheduler_SMP_Extract_from_scheduled( &self->Base.Base, &node->Base.Base );
  /* Not removing it from Ready since the node could go in the READY state */
}

static inline void _Scheduler_strong_APA_Extract_from_ready(
  Scheduler_Context *context,
  Scheduler_Node    *node_to_extract
)
{
  Scheduler_strong_APA_Node    *node;

  node = _Scheduler_strong_APA_Node_downcast( node_to_extract );

  if( !_Chain_Is_node_off_chain( &node->Ready_node ) ) {
    _Chain_Extract_unprotected( &node->Ready_node );
    _Chain_Set_off_chain( &node->Ready_node );
  }

}

static inline Scheduler_Node* _Scheduler_strong_APA_Get_lowest_reachable(
  Scheduler_strong_APA_Context *self,
  uint32_t                      front,
  uint32_t                      rear,
  Per_CPU_Control             **cpu_to_preempt
)
{
  Scheduler_Node              *lowest_reachable = NULL;
  Priority_Control             max_priority_num;
  uint32_t	               cpu_max;
  uint32_t	               cpu_index;
  Thread_Control              *curr_thread;
  Per_CPU_Control             *curr_CPU;
  Priority_Control             curr_priority;
  Scheduler_Node              *curr_node;
  Scheduler_strong_APA_Node   *curr_strong_node;
  Scheduler_strong_APA_CPU    *CPU;

  /* Max (Lowest) priority encountered so far */
  max_priority_num = 0;
  CPU = self->CPU;
  cpu_max = _SMP_Get_processor_maximum();

  while ( front <= rear ) {
    curr_CPU = CPU[ front ].cpu;
    front = front + 1;

    curr_node = _Scheduler_strong_APA_Get_scheduled( self, curr_CPU );
    curr_thread = curr_node->user;

    curr_priority = _Scheduler_Node_get_priority( curr_node );
    curr_priority = SCHEDULER_PRIORITY_PURIFY( curr_priority );

    curr_strong_node = _Scheduler_strong_APA_Node_downcast( curr_node );

    if ( curr_priority > max_priority_num ) {
      lowest_reachable = curr_node;
      max_priority_num = curr_priority;
      *cpu_to_preempt = curr_CPU;
    }

    if ( !curr_thread->is_idle ) {
      for ( cpu_index = 0 ; cpu_index < cpu_max ; ++cpu_index ) {
        if ( _Processor_mask_Is_set( &curr_strong_node->Affinity, cpu_index ) ) {
          /* Checks if the thread_CPU is in the affinity set of the node */
          Per_CPU_Control *cpu = _Per_CPU_Get_by_index( cpu_index );
          if (
            _Per_CPU_Is_processor_online( cpu ) &&
            CPU[ cpu_index ].visited == false )
          {
            rear = rear + 1;
            CPU[ rear ].cpu = cpu;
            CPU[ cpu_index ].visited = true;
            CPU[ cpu_index ].preempting_node = curr_node;
          }
        }
      }
    }
  }
  /*
   * Since it is not allowed for a task to have an empty affinity set,
   * there would always be a lowest_reachable task, hence it would not be NULL
   */
  _Assert( lowest_reachable != NULL );

  return lowest_reachable;
}

static inline bool _Scheduler_strong_APA_Do_enqueue(
  Scheduler_Context *context,
  Scheduler_Node    *lowest_reachable,
  Scheduler_Node    *node,
  Priority_Control  insert_priority,
  Per_CPU_Control  *cpu_to_preempt
)
{
  bool                          needs_help;
  Priority_Control              node_priority;
  Priority_Control              lowest_priority;
  Scheduler_strong_APA_CPU     *CPU;
  Scheduler_Node               *curr_node;
  /* The first node that gets removed from the cpu */
  Scheduler_Node               *first_node;
  Scheduler_strong_APA_Node    *curr_strong_node;
  Per_CPU_Control              *curr_CPU;
  Scheduler_strong_APA_Context *self;
  Scheduler_Node               *next_node;


  self = _Scheduler_strong_APA_Get_self( context );
  CPU = self->CPU;

  _Scheduler_SMP_Node_change_state( node, SCHEDULER_SMP_NODE_READY );

  node_priority = _Scheduler_Node_get_priority( node );
  node_priority = SCHEDULER_PRIORITY_PURIFY( node_priority );

  if( lowest_reachable == NULL ) {
    /*
     * This means the affinity set of the newly arrived node
     * is empty.
     */
    lowest_priority = UINT64_MAX;
  } else {
    lowest_priority =  _Scheduler_Node_get_priority( lowest_reachable );
    lowest_priority = SCHEDULER_PRIORITY_PURIFY( lowest_priority );
  }

  if ( lowest_priority > node_priority ) {
    /*
     * Backtrack on the path from
     * _Thread_Get_CPU(lowest_reachable->user) to lowest_reachable, shifting
     * along every task
     */

    curr_node = CPU[ _Per_CPU_Get_index( cpu_to_preempt ) ].preempting_node;
    curr_strong_node = _Scheduler_strong_APA_Node_downcast( curr_node );
    curr_strong_node->cpu_to_preempt = cpu_to_preempt;

    /* Save which cpu to preempt in cpu_to_preempt value of the node */
    while ( curr_node != node ) {
      curr_CPU = _Thread_Get_CPU( curr_node->user );
      curr_node = CPU[ _Per_CPU_Get_index( curr_CPU ) ].preempting_node;
      curr_strong_node = _Scheduler_strong_APA_Node_downcast( curr_node );
      curr_strong_node->cpu_to_preempt =  curr_CPU;
     }

    curr_CPU = curr_strong_node->cpu_to_preempt;
    next_node = _Scheduler_strong_APA_Get_scheduled( self, curr_CPU );

    node_priority = _Scheduler_Node_get_priority( curr_node );
    node_priority = SCHEDULER_PRIORITY_PURIFY( node_priority );

    _Scheduler_SMP_Enqueue_to_scheduled(
      context,
      curr_node,
      node_priority,
      next_node,
      _Scheduler_SMP_Insert_scheduled,
      _Scheduler_strong_APA_Move_from_scheduled_to_ready,
      _Scheduler_strong_APA_Move_from_ready_to_scheduled,
      _Scheduler_strong_APA_Allocate_processor,
      _Scheduler_strong_APA_Get_idle,
      _Scheduler_strong_APA_Release_idle
    );

    curr_node = next_node;
    first_node = curr_node;
    curr_strong_node = _Scheduler_strong_APA_Node_downcast( curr_node );

    while ( curr_node != lowest_reachable ) {
      Thread_Control *next_node_idle;

      curr_CPU = curr_strong_node->cpu_to_preempt;
      next_node = _Scheduler_strong_APA_Get_scheduled( self, curr_CPU );
      next_node_idle = _Scheduler_Release_idle_thread_if_necessary(
        next_node,
        _Scheduler_strong_APA_Release_idle,
        context
      );
      /* curr_node preempts the next_node; */
      _Scheduler_SMP_Preempt(
        context,
        curr_node,
        next_node,
        next_node_idle,
        _Scheduler_strong_APA_Allocate_processor
      );

      if(curr_node == first_node) {
        _Scheduler_strong_APA_Move_from_ready_to_scheduled(context, first_node);
      }
      curr_node = next_node;
      curr_strong_node = _Scheduler_strong_APA_Node_downcast( curr_node );
    }

    _Scheduler_strong_APA_Move_from_scheduled_to_ready( context, lowest_reachable );

    needs_help = false;
  } else {
    needs_help = true;
  }

  /* Add it to Ready chain since it is now either scheduled or just ready. */
  _Scheduler_strong_APA_Insert_ready( context,node, insert_priority );

  return needs_help;
}

/*
 * BFS Algorithm for task arrival
 * Enqueue node either in the scheduled chain or in the ready chain.
 * node is the newly arrived node and is currently not scheduled.
 */
static inline bool _Scheduler_strong_APA_Enqueue(
  Scheduler_Context *context,
  Scheduler_Node    *node,
  Priority_Control   insert_priority
)
{
  Scheduler_strong_APA_Context *self;
  Scheduler_strong_APA_CPU     *CPU;
  uint32_t	                cpu_max;
  uint32_t              	cpu_index;
  Per_CPU_Control              *cpu_to_preempt = NULL;
  Scheduler_Node               *lowest_reachable;
  Scheduler_strong_APA_Node    *strong_node;

  /* Denotes front and rear of the queue */
  uint32_t	front;
  uint32_t	rear;

  front = 0;
  rear = -1;

  self = _Scheduler_strong_APA_Get_self( context );
  strong_node = _Scheduler_strong_APA_Node_downcast( node );
  cpu_max = _SMP_Get_processor_maximum();
  CPU = self->CPU;

  for ( cpu_index = 0 ; cpu_index < cpu_max ; ++cpu_index ) {
    CPU[ cpu_index ].visited = false;

    /* Checks if the thread_CPU is in the affinity set of the node */
    if ( _Processor_mask_Is_set( &strong_node->Affinity, cpu_index ) ) {
      Per_CPU_Control *cpu = _Per_CPU_Get_by_index( cpu_index );

      if ( _Per_CPU_Is_processor_online( cpu ) ) {
        rear = rear + 1;
        CPU[ rear ].cpu = cpu;
        CPU[ cpu_index ].visited = true;
        CPU[ cpu_index ].preempting_node = node;
      }
    }
  }

  lowest_reachable = _Scheduler_strong_APA_Get_lowest_reachable(
                       self,
                       front,
                       rear,
                       &cpu_to_preempt
                     );
  /*
   * Since it is not allowed for a task to have an empty affinity set,
   * there would always be a lowest_reachable task, hence cpu_to_preempt
   * would not be NULL.
   */
  _Assert( cpu_to_preempt != NULL );
  return _Scheduler_strong_APA_Do_enqueue(
           context,
           lowest_reachable,
           node,
           insert_priority,
           cpu_to_preempt
         );
}

static inline void _Scheduler_strong_APA_Enqueue_scheduled(
  Scheduler_Context *context,
  Scheduler_Node    *node,
  Priority_Control   insert_priority
)
{
  _Scheduler_SMP_Enqueue_scheduled(
    context,
    node,
    insert_priority,
    _Scheduler_SMP_Priority_less_equal,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Get_highest_ready,
    _Scheduler_strong_APA_Insert_ready,
    _Scheduler_SMP_Insert_scheduled,
    _Scheduler_strong_APA_Move_from_ready_to_scheduled,
    _Scheduler_strong_APA_Allocate_processor,
    _Scheduler_strong_APA_Get_idle,
    _Scheduler_strong_APA_Release_idle
  );
}

static inline bool _Scheduler_strong_APA_Do_ask_for_help(
  Scheduler_Context *context,
  Thread_Control    *the_thread,
  Scheduler_Node    *node
)
{
  return _Scheduler_SMP_Ask_for_help(
    context,
    the_thread,
    node,
    _Scheduler_SMP_Priority_less_equal,
    _Scheduler_strong_APA_Insert_ready,
    _Scheduler_SMP_Insert_scheduled,
    _Scheduler_strong_APA_Move_from_scheduled_to_ready,
    _Scheduler_strong_APA_Get_lowest_scheduled,
    _Scheduler_strong_APA_Allocate_processor,
    _Scheduler_strong_APA_Release_idle
  );
}

static inline  void  _Scheduler_strong_APA_Do_set_affinity(
  Scheduler_Context *context,
  Scheduler_Node    *node_base,
  void              *arg
)
{
  Scheduler_strong_APA_Node *node;

  node = _Scheduler_strong_APA_Node_downcast( node_base );
  node->Affinity = *( (const Processor_mask *) arg );
}

void _Scheduler_strong_APA_Initialize( const Scheduler_Control *scheduler )
{
  Scheduler_strong_APA_Context *self =
      _Scheduler_strong_APA_Get_context( scheduler );

  _Scheduler_SMP_Initialize( &self->Base );
  _Chain_Initialize_empty( &self->Ready );
}

void _Scheduler_strong_APA_Yield(
  const Scheduler_Control *scheduler,
  Thread_Control          *thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Yield(
    context,
    thread,
    node,
    _Scheduler_strong_APA_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Enqueue,
    _Scheduler_strong_APA_Enqueue_scheduled
  );
}

void _Scheduler_strong_APA_Block(
  const Scheduler_Control *scheduler,
  Thread_Control          *thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  /*
   * Needed in case the node is scheduled node, since _SMP_Block only extracts
   * from the SMP scheduled chain and from the Strong APA Ready_chain
   * when the node is ready. But the Strong APA Ready_chain stores both
   * ready and scheduled nodes.
   */
  _Scheduler_strong_APA_Extract_from_ready(context, node);

  _Scheduler_SMP_Block(
    context,
    thread,
    node,
    _Scheduler_strong_APA_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Get_highest_ready,
    _Scheduler_strong_APA_Move_from_ready_to_scheduled,
    _Scheduler_strong_APA_Allocate_processor,
    _Scheduler_strong_APA_Get_idle
  );
}

void _Scheduler_strong_APA_Unblock(
  const Scheduler_Control *scheduler,
  Thread_Control          *thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Unblock(
    context,
    thread,
    node,
    _Scheduler_strong_APA_Do_update,
    _Scheduler_strong_APA_Enqueue,
    _Scheduler_strong_APA_Release_idle
  );
}

void _Scheduler_strong_APA_Update_priority(
  const Scheduler_Control *scheduler,
  Thread_Control          *thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Update_priority(
    context,
    thread,
    node,
    _Scheduler_strong_APA_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Do_update,
    _Scheduler_strong_APA_Enqueue,
    _Scheduler_strong_APA_Enqueue_scheduled,
    _Scheduler_strong_APA_Do_ask_for_help
  );
}

bool _Scheduler_strong_APA_Ask_for_help(
  const Scheduler_Control *scheduler,
  Thread_Control          *the_thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  return _Scheduler_strong_APA_Do_ask_for_help(
    context,
    the_thread,
    node
  );
}

void _Scheduler_strong_APA_Reconsider_help_request(
  const Scheduler_Control *scheduler,
  Thread_Control          *the_thread,
  Scheduler_Node          *node
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Reconsider_help_request(
    context,
    the_thread,
    node,
    _Scheduler_strong_APA_Extract_from_ready
  );
}

void _Scheduler_strong_APA_Withdraw_node(
  const Scheduler_Control *scheduler,
  Thread_Control          *the_thread,
  Scheduler_Node          *node,
  Thread_Scheduler_state   next_state
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Withdraw_node(
    context,
    the_thread,
    node,
    next_state,
    _Scheduler_strong_APA_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Get_highest_ready,
    _Scheduler_strong_APA_Move_from_ready_to_scheduled,
    _Scheduler_strong_APA_Allocate_processor,
    _Scheduler_strong_APA_Get_idle
  );
}

void _Scheduler_strong_APA_Make_sticky(
  const Scheduler_Control *scheduler,
  Thread_Control          *the_thread,
  Scheduler_Node          *node
)
{
  _Scheduler_SMP_Make_sticky(
    scheduler,
    the_thread,
    node,
    _Scheduler_strong_APA_Do_update,
    _Scheduler_strong_APA_Enqueue
  );
}

void _Scheduler_strong_APA_Clean_sticky(
  const Scheduler_Control *scheduler,
  Thread_Control          *the_thread,
  Scheduler_Node          *node
)
{
  _Scheduler_SMP_Clean_sticky(
    scheduler,
    the_thread,
    node,
    _Scheduler_SMP_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Get_highest_ready,
    _Scheduler_strong_APA_Move_from_ready_to_scheduled,
    _Scheduler_strong_APA_Allocate_processor,
    _Scheduler_strong_APA_Get_idle,
    _Scheduler_strong_APA_Release_idle
  );
}

static inline void _Scheduler_strong_APA_Register_idle(
  Scheduler_Context *context,
  Scheduler_Node    *idle_base,
  Per_CPU_Control   *cpu
)
{
  Scheduler_strong_APA_Context *self;
  self = _Scheduler_strong_APA_Get_self( context );

  _Scheduler_strong_APA_Set_scheduled( self, idle_base, cpu );
}

void _Scheduler_strong_APA_Add_processor(
  const Scheduler_Control *scheduler,
  Thread_Control          *idle
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Add_processor(
    context,
    idle,
    _Scheduler_strong_APA_Has_ready,
    _Scheduler_strong_APA_Enqueue_scheduled,
    _Scheduler_strong_APA_Register_idle
  );
}

void _Scheduler_strong_APA_Start_idle(
  const Scheduler_Control *scheduler,
  Thread_Control          *idle,
  Per_CPU_Control         *cpu
)
{
  Scheduler_Context *context;

  context = _Scheduler_Get_context( scheduler );

  _Scheduler_SMP_Do_start_idle(
    context,
    idle,
    cpu,
    _Scheduler_strong_APA_Register_idle
  );
}

Thread_Control *_Scheduler_strong_APA_Remove_processor(
  const Scheduler_Control *scheduler,
  Per_CPU_Control         *cpu
)
{
  Scheduler_Context *context = _Scheduler_Get_context( scheduler );

  return _Scheduler_SMP_Remove_processor(
    context,
    cpu,
    _Scheduler_strong_APA_Extract_from_scheduled,
    _Scheduler_strong_APA_Extract_from_ready,
    _Scheduler_strong_APA_Enqueue,
    _Scheduler_strong_APA_Get_idle,
    _Scheduler_strong_APA_Release_idle
  );
}

void _Scheduler_strong_APA_Node_initialize(
  const Scheduler_Control *scheduler,
  Scheduler_Node          *node,
  Thread_Control          *the_thread,
  Priority_Control         priority
)
{
  Scheduler_SMP_Node *smp_node;
  Scheduler_strong_APA_Node *strong_node;

  smp_node = _Scheduler_SMP_Node_downcast( node );
  strong_node = _Scheduler_strong_APA_Node_downcast( node );

  _Scheduler_SMP_Node_initialize( scheduler, smp_node, the_thread, priority );

  _Processor_mask_Assign(
    &strong_node->Affinity,
   _SMP_Get_online_processors()
  );
}

Status_Control _Scheduler_strong_APA_Set_affinity(
  const Scheduler_Control *scheduler,
  Thread_Control          *thread,
  Scheduler_Node          *node_base,
  const Processor_mask    *affinity
)
{
  Scheduler_Context         *context;
  Scheduler_strong_APA_Node *node;
  Processor_mask             local_affinity;

  context = _Scheduler_Get_context( scheduler );
  _Processor_mask_And( &local_affinity, &context->Processors, affinity );

  if ( _Processor_mask_Is_zero( &local_affinity ) ) {
    return STATUS_INVALID_NUMBER;
  }

  node = _Scheduler_strong_APA_Node_downcast( node_base );

  if ( _Processor_mask_Is_equal( &node->Affinity, affinity ) )
    return STATUS_SUCCESSFUL;	/* Nothing to do. Return true. */

 _Processor_mask_Assign( &node->Affinity, &local_affinity );

 _Scheduler_SMP_Set_affinity(
   context,
   thread,
   node_base,
   &local_affinity,
   _Scheduler_strong_APA_Do_set_affinity,
   _Scheduler_strong_APA_Extract_from_scheduled,
   _Scheduler_strong_APA_Extract_from_ready,
   _Scheduler_strong_APA_Get_highest_ready,
   _Scheduler_strong_APA_Move_from_ready_to_scheduled,
   _Scheduler_strong_APA_Enqueue,
   _Scheduler_strong_APA_Allocate_processor,
   _Scheduler_strong_APA_Get_idle,
   _Scheduler_strong_APA_Release_idle
 );

  return STATUS_SUCCESSFUL;
}