/**************************************************************************/
/*                                                                        */
/*       Copyright (c) Microsoft Corporation. All rights reserved.        */
/*                                                                        */
/*       This software is licensed under the Microsoft Software License   */
/*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
/*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
/*       and in the root directory of this software.                      */
/*                                                                        */
/**************************************************************************/


/**************************************************************************/
/**************************************************************************/
/**                                                                       */ 
/** ThreadX Component                                                     */ 
/**                                                                       */
/**   Thread                                                              */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/

#define TX_SOURCE_CODE
#define TX_THREAD_SMP_SOURCE_CODE


/* Include necessary system files.  */

#include "tx_api.h"
#include "tx_timer.h"
#include "tx_thread.h"
#include "tx_trace.h"


/**************************************************************************/ 
/*                                                                        */ 
/*  FUNCTION                                               RELEASE        */ 
/*                                                                        */ 
/*    _tx_thread_time_slice                              PORTABLE SMP     */ 
/*                                                           6.0.1        */
/*  AUTHOR                                                                */
/*                                                                        */
/*    William E. Lamie, Microsoft Corporation                             */
/*                                                                        */
/*  DESCRIPTION                                                           */
/*                                                                        */ 
/*    This function moves the currently executing thread to the end of    */ 
/*    the threads ready at the same priority level as a result of a       */ 
/*    time-slice interrupt.  If no other thread of the same priority is   */ 
/*    ready, this function simply returns.                                */ 
/*                                                                        */ 
/*  INPUT                                                                 */ 
/*                                                                        */ 
/*    None                                                                */ 
/*                                                                        */ 
/*  OUTPUT                                                                */ 
/*                                                                        */ 
/*    None                                                                */ 
/*                                                                        */ 
/*  CALLS                                                                 */ 
/*                                                                        */ 
/*    _tx_thread_smp_rebalance_execute_list Rebalance the execution list  */ 
/*                                                                        */ 
/*  CALLED BY                                                             */ 
/*                                                                        */ 
/*    _tx_timer_interrupt                   Timer interrupt handling      */ 
/*                                                                        */ 
/*  RELEASE HISTORY                                                       */ 
/*                                                                        */ 
/*    DATE              NAME                      DESCRIPTION             */
/*                                                                        */
/*  06-30-2020     William E. Lamie         Initial Version 6.0.1         */
/*                                                                        */
/**************************************************************************/
VOID  _tx_thread_time_slice(VOID)
{

ULONG           core_index, current_core;
UINT            priority;
TX_THREAD       *thread_ptr;
TX_THREAD       *next_thread;
TX_THREAD       *previous_thread;
TX_THREAD       *head_ptr;
TX_THREAD       *tail_ptr;
UINT            loop_finished;
UINT            rebalance;
ULONG           excluded;
#ifdef TX_ENABLE_EVENT_TRACE
ULONG           system_state;
UINT            preempt_disable;
#endif


    /* Quick check for expiration.  */
#if TX_THREAD_SMP_MAX_CORES == 1
    if (_tx_timer_time_slice[0] != 0)
    {
#endif
#if TX_THREAD_SMP_MAX_CORES == 2
    if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)))
    {
#endif
#if TX_THREAD_SMP_MAX_CORES == 3
    if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)) || (_tx_timer_time_slice[2] != ((ULONG) 0)))
    {
#endif 
#if TX_THREAD_SMP_MAX_CORES == 4
    if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)) || (_tx_timer_time_slice[2] != ((ULONG) 0)) || (_tx_timer_time_slice[3] != ((ULONG) 0)))
    {
#endif 

        /* Initialize the rebalance flag to false.  */
        rebalance =  TX_FALSE;

        /* Get the core index.  */
        current_core =  TX_SMP_CORE_ID;

        /* Loop to process all time-slices.  */

#ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX

        for (core_index = ((ULONG) 0); core_index < ((ULONG) TX_THREAD_SMP_MAX_CORES); core_index++)
#else

        for (core_index = ((ULONG) 0); core_index < _tx_thread_smp_max_cores; core_index++)
#endif
        {

            /* Determine if there is a time-slice active on this core.  */
            if (_tx_timer_time_slice[core_index] != ((ULONG) 0))
            {
        
                /* Time-slice is active, decrement it for this core.  */
                _tx_timer_time_slice[core_index]--;

                /* Has the time-slice expired?  */
                if (_tx_timer_time_slice[core_index] == ((ULONG) 0))
                {

                    /* Yes, time-slice on this core has expired.  */

                    /* Pickup the current thread pointer.  */
                    thread_ptr =  _tx_thread_current_ptr[core_index];

                    /* Make sure the thread is still active, i.e. not suspended.  */
                    if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_state == TX_READY) && (thread_ptr -> tx_thread_time_slice != ((ULONG) 0)))
                    {

                        /* Yes, thread is still active and time-slice has expired.  */

                        /* Setup a fresh time-slice for the thread.  */
                        thread_ptr -> tx_thread_time_slice =  thread_ptr -> tx_thread_new_time_slice;

                        /* Reset the actual time-slice variable.  */
                        _tx_timer_time_slice[core_index] =  thread_ptr -> tx_thread_time_slice;
    
#ifdef TX_THREAD_SMP_DEBUG_ENABLE

                        /* Debug entry.  */
                        _tx_thread_smp_debug_entry_insert(10, 0, thread_ptr);
#endif
                
#ifdef TX_ENABLE_STACK_CHECKING 

                        /* Check this thread's stack.  */
                        TX_THREAD_STACK_CHECK(thread_ptr)
#endif

#ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO

                        /* Increment the thread's time-slice counter.  */
                        thread_ptr -> tx_thread_performance_time_slice_count++;

                        /* Increment the total number of thread time-slice operations.  */
                        _tx_thread_performance_time_slice_count++;
#endif

                        /* Setup the priority.  */
                        priority =  thread_ptr -> tx_thread_priority;

                        /* Determine if preemption-threshold is set. If so, don't time-slice.  */
                        if (priority == thread_ptr -> tx_thread_preempt_threshold)
                        {
            
                            /* Preemption-threshold is not set.  */
    
                            /* Pickup the next thread.  */
                            next_thread =  thread_ptr -> tx_thread_ready_next;

                            /* Pickup the head of the list.  */
                            head_ptr =  _tx_thread_priority_list[priority];

                            /* Pickup the list tail.  */
                            tail_ptr =  head_ptr -> tx_thread_ready_previous;

                            /* Determine if this thread is not the tail pointer.  */
                            if (thread_ptr != tail_ptr)
                            {

                                /* Not the tail pointer, this thread must be moved to the end of the ready list.  */

                                /* Determine if this thread is at the head of the list.  */
                                if (head_ptr == thread_ptr)
                                {
        
                                    /* Simply move the head pointer to put this thread at the end of the ready list at this priority.  */
                                    _tx_thread_priority_list[priority] =  next_thread;
                                }
                                else
                                {

                                    /* Now we need to remove this thread from its current position and place it at the end of the list.   */

                                    /* Pickup the previous thread pointer.  */
                                    previous_thread =  thread_ptr -> tx_thread_ready_previous;

                                    /* Remove the thread from the ready list.  */
                                    next_thread -> tx_thread_ready_previous =    previous_thread;
                                    previous_thread -> tx_thread_ready_next =    next_thread;

                                    /* Insert the thread at the end of the list.  */
                                    tail_ptr -> tx_thread_ready_next =         thread_ptr;
                                    head_ptr -> tx_thread_ready_previous =     thread_ptr;
                                    thread_ptr -> tx_thread_ready_previous =   tail_ptr;
                                    thread_ptr -> tx_thread_ready_next =       head_ptr;
                                }

                                /* Make sure the current core execute pointer is still this thread.  If not, a higher priority thread has already
                                   preempted it either from another ISR or from the timer processing.  */
                                if (_tx_thread_execute_ptr[core_index] != thread_ptr)
                                {
                                
                                    /* Set the rebalance flag.  */
                                    rebalance =  TX_TRUE;
                                }        
                                
                                /* Determine if the rebalance flag has been set already.  If so, don't bother trying to update the 
                                   execute list from this routine.  */
                                if (rebalance == TX_FALSE)
                                {

                                    /* Set the loop finished flag to false.  */
                                    loop_finished =  TX_FALSE;

                                    /* Determine if there is a thread at the same priority that isn't currently executing.  */
                                    do
                                    {

                                        /* Isolate the exclusion for this core.  */
                                        excluded =  (next_thread -> tx_thread_smp_cores_excluded >> core_index) & ((ULONG) 1);
                                      
                                        /* Determine if the next thread has preemption-threshold set.  */
                                        if (next_thread -> tx_thread_preempt_threshold < next_thread -> tx_thread_priority)
                                        {
            
                                            /* Set the rebalance flag.  */
                                            rebalance =  TX_TRUE;

                                            /* Get out of the loop.  */
                                            loop_finished =  TX_TRUE;
                                        }
                                        
                                        /* Determine if the next thread is excluded from running on this core.  */
                                        else if (excluded == ((ULONG) 1))
                                        {
            
                                            /* Set the rebalance flag.  */
                                            rebalance =  TX_TRUE;
                
                                            /* Get out of the loop.  */
                                            loop_finished =  TX_TRUE;
                                        }
                                        else
                                        {
                                    
                                            /* Is the next thread not scheduled  */
                                            if (next_thread != _tx_thread_execute_ptr[next_thread -> tx_thread_smp_core_mapped])
                                            {
       
                                                /* Remember this index in the thread control block.  */
                                                next_thread -> tx_thread_smp_core_mapped =  core_index;

                                                /* Setup the entry in the execution list.  */
                                                _tx_thread_execute_ptr[core_index] =  next_thread;

#ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT

                                                /* Determine if we need to preempt the core.  */
                                                if (core_index != current_core)
                                                {

                                                    /* Preempt the mapped thread.  */
                                                    _tx_thread_smp_core_preempt(core_index);
                                                }
#endif
                                                /* Get out of the loop.  */
                                                loop_finished =  TX_TRUE;
                                            }            
                                        }

                                        /* Is the loop fininshed?  */
                                        if (loop_finished == TX_TRUE)
                                        {
                                    
                                            /* Yes, break out of the loop.  */
                                            break;
                                        }
                    
                                        /* Move to the next thread at this priority.  */
                                        next_thread =  next_thread -> tx_thread_ready_next;
        
                                    } while (next_thread != thread_ptr);
                                }
                            }

#ifdef TX_THREAD_SMP_DEBUG_ENABLE

                            /* Debug entry.  */
                            _tx_thread_smp_debug_entry_insert(11, 0, thread_ptr);
#endif
                        }
                    }
                }
            }
        }

        /* Determine if rebalance was set.  */
        if (rebalance == TX_TRUE)
        {

            /* Call the rebalance routine. This routine maps cores and ready threads.  */
            _tx_thread_smp_rebalance_execute_list(current_core);
        }

#ifdef TX_ENABLE_EVENT_TRACE

        /* Pickup the volatile information.  */
        system_state =  TX_THREAD_GET_SYSTEM_STATE();
        preempt_disable =  _tx_thread_preempt_disable;
   
        /* Insert this event into the trace buffer.  */
        TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIME_SLICE, _tx_thread_execute_ptr[0], system_state, preempt_disable, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_INTERNAL_EVENTS)
#endif
        
#if TX_THREAD_SMP_MAX_CORES == 1
    }
#endif
#if TX_THREAD_SMP_MAX_CORES == 2
    }
#endif
#if TX_THREAD_SMP_MAX_CORES == 3
    }
#endif 
#if TX_THREAD_SMP_MAX_CORES == 4
    }
#endif 
}

