Potential xNumOfOverflows bug when tickless idle is enabled

Hello!

I am currently working with both FreeRTOS 9 and 10, and have uncovered something that I suspect is a bug concerning the global variable “xNumOfOverflows” defined and managed by tasks.c.

During tick increments in:

BaseType_t xTaskIncrementTick( void )

I notice that there is very explicit handling for overflow detection.

if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */
{
    taskSWITCH_DELAYED_LISTS();
}

This macro appears to be the only location that increments the overflow counter; however, it’s not the only place where the overflow counter is used.

#define taskSWITCH_DELAYED_LISTS()																	\
{																									\
	List_t *pxTemp;																					\
																									\
	/* The delayed tasks list should be empty when the lists are switched. */						\
	configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) );										\
																									\
	pxTemp = pxDelayedTaskList;																		\
	pxDelayedTaskList = pxOverflowDelayedTaskList;													\
	pxOverflowDelayedTaskList = pxTemp;																\
	xNumOfOverflows++;																				\
	prvResetNextTaskUnblockTime();																	\
}

I cannot find evidence that tickless idle is taking the same care to detect and increment xNumOfOverflows.

void vTaskStepTick( const TickType_t xTicksToJump )
{
    /* Correct the tick count value after a period during which the tick
    was suppressed. Note this does *not* call the tick hook function for
    each stepped tick. */
    configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime );
    xTickCount += xTicksToJump;
    traceINCREASE_TICK_COUNT( xTicksToJump );
}

It looks like xTickCount is just naively incrementing by ticks.

Other than what may be going on in the kernel due to xNumOfOverflows potentially being inaccurate, it also calls in the question of whether the following function is safe to use.

void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut )
{
   /* For internal use only as it does not use a critical section. */
   pxTimeOut->xOverflowCount = xNumOfOverflows;
   pxTimeOut->xTimeOnEntering = xTickCount;
}

Have I uncovered a situation where overflow is incorrectly being handled when in tickless idle? If not, can some explanation be provided to explain what I may be missing?

I’ve not double checked the code so tell me if you think this is wrong.

There is a function called something like prvGetExpectedIdleTime() that is used by tickless idle to determine the time at which the processor knows it must next perform some processing. For example, if three tasks are in the blocked state, the first will unblock in 100ms, the second in 1000ms and the third in 10000ms, then prvGetExpectedIdleTime() will return 100ms as that is the closest time it knows it has something to do - and the processor will be configured to wake from sleep mode in 100ms. This is the worst case time - an interrupt can always bring the processor out of sleep mode at an earlier time. However, the “next known unblock time” is already capped to the time at which the tick count overflows, so prvGetExpectedIdleTime() can never return a value that would cause vTaskStepTick() to overflow the tick count. It would be interesting to see if all corner cases were tested here - but use of configEXPECTED_IDLE_TIME_BEFORE_SLEEP - which is the minimum sleep period allowed - should prevent vTaskStepTick() ending up exactly on the overflow value.