/**************************************************************************/ /* */ /* 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 */ /** */ /** Timer */ /** */ /**************************************************************************/ /**************************************************************************/ #define TX_SOURCE_CODE /* Include necessary system files. */ #include "tx_api.h" #include "tx_timer.h" #include "tx_thread.h" /**************************************************************************/ /* */ /* FUNCTION RELEASE */ /* */ /* _tx_timer_thread_entry PORTABLE C */ /* 6.1 */ /* AUTHOR */ /* */ /* William E. Lamie, Microsoft Corporation */ /* */ /* DESCRIPTION */ /* */ /* This function manages thread and application timer expirations. */ /* Actually, from this thread's point of view, there is no difference. */ /* */ /* INPUT */ /* */ /* timer_thread_input Used just for verification */ /* */ /* OUTPUT */ /* */ /* None */ /* */ /* CALLS */ /* */ /* Timer Expiration Function */ /* _tx_thread_system_suspend Thread suspension */ /* _tx_thread_system_ni_suspend Non-interruptable suspend thread */ /* _tx_timer_system_activate Timer reactivate processing */ /* */ /* CALLED BY */ /* */ /* ThreadX Scheduler */ /* */ /* RELEASE HISTORY */ /* */ /* DATE NAME DESCRIPTION */ /* */ /* 05-19-2020 William E. Lamie Initial Version 6.0 */ /* 09-30-2020 Yuxin Zhou Modified comment(s), */ /* resulting in version 6.1 */ /* */ /**************************************************************************/ #ifndef TX_TIMER_PROCESS_IN_ISR VOID _tx_timer_thread_entry(ULONG timer_thread_input) { TX_INTERRUPT_SAVE_AREA TX_TIMER_INTERNAL *expired_timers; TX_TIMER_INTERNAL *reactivate_timer; TX_TIMER_INTERNAL *next_timer; TX_TIMER_INTERNAL *previous_timer; TX_TIMER_INTERNAL *current_timer; VOID (*timeout_function)(ULONG id); ULONG timeout_param = ((ULONG) 0); TX_THREAD *thread_ptr; #ifdef TX_REACTIVATE_INLINE TX_TIMER_INTERNAL **timer_list; /* Timer list pointer */ UINT expiration_time; /* Value used for pointer offset*/ ULONG delta; #endif #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO TX_TIMER *timer_ptr; #endif /* Make sure the timer input is correct. This also gets rid of the silly compiler warnings. */ if (timer_thread_input == TX_TIMER_ID) { /* Yes, valid thread entry, proceed... */ /* Now go into an infinite loop to process timer expirations. */ while (TX_LOOP_FOREVER) { /* First, move the current list pointer and clear the timer expired value. This allows the interrupt handling portion to continue looking for timer expirations. */ TX_DISABLE /* Save the current timer expiration list pointer. */ expired_timers = *_tx_timer_current_ptr; /* Modify the head pointer in the first timer in the list, if there is one! */ if (expired_timers != TX_NULL) { expired_timers -> tx_timer_internal_list_head = &expired_timers; } /* Set the current list pointer to NULL. */ *_tx_timer_current_ptr = TX_NULL; /* Move the current pointer up one timer entry wrap if we get to the end of the list. */ _tx_timer_current_ptr = TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, 1); if (_tx_timer_current_ptr == _tx_timer_list_end) { _tx_timer_current_ptr = _tx_timer_list_start; } /* Clear the expired flag. */ _tx_timer_expired = TX_FALSE; /* Restore interrupts temporarily. */ TX_RESTORE /* Disable interrupts again. */ TX_DISABLE /* Next, process the expiration of the associated timers at this time slot. */ while (expired_timers != TX_NULL) { /* Something is on the list. Remove it and process the expiration. */ current_timer = expired_timers; /* Pickup the next timer. */ next_timer = expired_timers -> tx_timer_internal_active_next; /* Set the reactivate_timer to NULL. */ reactivate_timer = TX_NULL; /* Determine if this is the only timer. */ if (current_timer == next_timer) { /* Yes, this is the only timer in the list. */ /* Set the head pointer to NULL. */ expired_timers = TX_NULL; } else { /* No, not the only expired timer. */ /* Remove this timer from the expired list. */ previous_timer = current_timer -> tx_timer_internal_active_previous; next_timer -> tx_timer_internal_active_previous = previous_timer; previous_timer -> tx_timer_internal_active_next = next_timer; /* Modify the next timer's list head to point at the current list head. */ next_timer -> tx_timer_internal_list_head = &expired_timers; /* Set the list head pointer. */ expired_timers = next_timer; } /* In any case, the timer is now off of the expired list. */ /* Determine if the timer has expired or if it is just a really big timer that needs to be placed in the list again. */ if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES) { /* Timer is bigger than the timer entries and must be rescheduled. */ #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO /* Increment the total expiration adjustments counter. */ _tx_timer_performance__expiration_adjust_count++; /* Determine if this is an application timer. */ if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout) { /* Derive the application timer pointer. */ /* Pickup the application timer pointer. */ TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr) /* Increment the number of expiration adjustments on this timer. */ if (timer_ptr -> tx_timer_id == TX_TIMER_ID) { timer_ptr -> tx_timer_performance__expiration_adjust_count++; } } #endif /* Decrement the remaining ticks of the timer. */ current_timer -> tx_timer_internal_remaining_ticks = current_timer -> tx_timer_internal_remaining_ticks - TX_TIMER_ENTRIES; /* Set the timeout function to NULL in order to bypass the expiration. */ timeout_function = TX_NULL; /* Make the timer appear that it is still active while interrupts are enabled. This will permit proper processing of a timer deactivate from an ISR. */ current_timer -> tx_timer_internal_list_head = &reactivate_timer; current_timer -> tx_timer_internal_active_next = current_timer; /* Setup the temporary timer list head pointer. */ reactivate_timer = current_timer; } else { /* Timer did expire. */ #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO /* Increment the total expirations counter. */ _tx_timer_performance_expiration_count++; /* Determine if this is an application timer. */ if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout) { /* Derive the application timer pointer. */ /* Pickup the application timer pointer. */ TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr) /* Increment the number of expirations on this timer. */ if (timer_ptr -> tx_timer_id == TX_TIMER_ID) { timer_ptr -> tx_timer_performance_expiration_count++; } } #endif /* Copy the calling function and ID into local variables before interrupts are re-enabled. */ timeout_function = current_timer -> tx_timer_internal_timeout_function; timeout_param = current_timer -> tx_timer_internal_timeout_param; /* Copy the reinitialize ticks into the remaining ticks. */ current_timer -> tx_timer_internal_remaining_ticks = current_timer -> tx_timer_internal_re_initialize_ticks; /* Determine if the timer should be reactivated. */ if (current_timer -> tx_timer_internal_remaining_ticks != ((ULONG) 0)) { /* Make the timer appear that it is still active while processing the expiration routine and with interrupts enabled. This will permit proper processing of a timer deactivate from both the expiration routine and an ISR. */ current_timer -> tx_timer_internal_list_head = &reactivate_timer; current_timer -> tx_timer_internal_active_next = current_timer; /* Setup the temporary timer list head pointer. */ reactivate_timer = current_timer; } else { /* Set the list pointer of this timer to NULL. This is used to indicate the timer is no longer active. */ current_timer -> tx_timer_internal_list_head = TX_NULL; } } /* Set pointer to indicate the expired timer that is currently being processed. */ _tx_timer_expired_timer_ptr = current_timer; /* Restore interrupts for timer expiration call. */ TX_RESTORE /* Call the timer-expiration function, if non-NULL. */ if (timeout_function != TX_NULL) { (timeout_function) (timeout_param); } /* Lockout interrupts again. */ TX_DISABLE /* Clear expired timer pointer. */ _tx_timer_expired_timer_ptr = TX_NULL; /* Determine if the timer needs to be reactivated. */ if (reactivate_timer == current_timer) { /* Reactivate the timer. */ #ifdef TX_TIMER_ENABLE_PERFORMANCE_INFO /* Determine if this timer expired. */ if (timeout_function != TX_NULL) { /* Increment the total reactivations counter. */ _tx_timer_performance_reactivate_count++; /* Determine if this is an application timer. */ if (current_timer -> tx_timer_internal_timeout_function != &_tx_thread_timeout) { /* Derive the application timer pointer. */ /* Pickup the application timer pointer. */ TX_USER_TIMER_POINTER_GET(current_timer, timer_ptr) /* Increment the number of expirations on this timer. */ if (timer_ptr -> tx_timer_id == TX_TIMER_ID) { timer_ptr -> tx_timer_performance_reactivate_count++; } } } #endif #ifdef TX_REACTIVATE_INLINE /* Calculate the amount of time remaining for the timer. */ if (current_timer -> tx_timer_internal_remaining_ticks > TX_TIMER_ENTRIES) { /* Set expiration time to the maximum number of entries. */ expiration_time = TX_TIMER_ENTRIES - ((UINT) 1); } else { /* Timer value fits in the timer entries. */ /* Set the expiration time. */ expiration_time = ((UINT) current_timer -> tx_timer_internal_remaining_ticks) - ((UINT) 1); } /* At this point, we are ready to put the timer back on one of the timer lists. */ /* Calculate the proper place for the timer. */ timer_list = TX_TIMER_POINTER_ADD(_tx_timer_current_ptr, expiration_time); if (TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(timer_list) >= TX_TIMER_INDIRECT_TO_VOID_POINTER_CONVERT(_tx_timer_list_end)) { /* Wrap from the beginning of the list. */ delta = TX_TIMER_POINTER_DIF(timer_list, _tx_timer_list_end); timer_list = TX_TIMER_POINTER_ADD(_tx_timer_list_start, delta); } /* Now put the timer on this list. */ if ((*timer_list) == TX_NULL) { /* This list is NULL, just put the new timer on it. */ /* Setup the links in this timer. */ current_timer -> tx_timer_internal_active_next = current_timer; current_timer -> tx_timer_internal_active_previous = current_timer; /* Setup the list head pointer. */ *timer_list = current_timer; } else { /* This list is not NULL, add current timer to the end. */ next_timer = *timer_list; previous_timer = next_timer -> tx_timer_internal_active_previous; previous_timer -> tx_timer_internal_active_next = current_timer; next_timer -> tx_timer_internal_active_previous = current_timer; current_timer -> tx_timer_internal_active_next = next_timer; current_timer -> tx_timer_internal_active_previous = previous_timer; } /* Setup list head pointer. */ current_timer -> tx_timer_internal_list_head = timer_list; #else /* Reactivate through the timer activate function. */ /* Clear the list head for the timer activate call. */ current_timer -> tx_timer_internal_list_head = TX_NULL; /* Activate the current timer. */ _tx_timer_system_activate(current_timer); #endif } /* Restore interrupts. */ TX_RESTORE /* Lockout interrupts again. */ TX_DISABLE } /* Finally, suspend this thread and wait for the next expiration. */ /* Determine if another expiration took place while we were in this thread. If so, process another expiration. */ if (_tx_timer_expired == TX_FALSE) { /* Otherwise, no timer expiration, so suspend the thread. */ /* Build pointer to the timer thread. */ thread_ptr = &_tx_timer_thread; /* Set the status to suspending, in order to indicate the suspension is in progress. */ thread_ptr -> tx_thread_state = TX_SUSPENDED; #ifdef TX_NOT_INTERRUPTABLE /* Call actual non-interruptable thread suspension routine. */ _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0)); /* Restore interrupts. */ TX_RESTORE #else /* Set the suspending flag. */ thread_ptr -> tx_thread_suspending = TX_TRUE; /* Increment the preempt disable count prior to suspending. */ _tx_thread_preempt_disable++; /* Restore interrupts. */ TX_RESTORE /* Call actual thread suspension routine. */ _tx_thread_system_suspend(thread_ptr); #endif } else { /* Restore interrupts. */ TX_RESTORE } } } #ifdef TX_SAFETY_CRITICAL /* If we ever get here, raise safety critical exception. */ TX_SAFETY_CRITICAL_EXCEPTION(__FILE__, __LINE__, 0); #endif } #endif