Enter Tickless Idle on portMAX_DELAY / Problem with xNextTaskUnblockTime holding wrong value.

christophneukom wrote on Monday, November 03, 2014:

I’m using FreerTOS 8.0.0 on a PIC24 and am attempting to use the tickless idle mode. It works fine except for one thing I don’t quite understand.

FreeRTOS is used for a simple data logger which is in sleep mode most of the time. It wakes up on an RTCC alarm every 10 seconds and takes a measurement which should take around 200ms. There are multiple UART interfaces used for communication during the measurement phase using queues to report their status to the main task. In the main task I’m using xQueueReceive timeouts to block until data has been sent or received by the UART drivers. Once the measurement is done, the main task blocks indefinitely on a semaphore given by the RTCC alarm ISR.

What I see happen is that the system does not call portSUPPRESS_TICKS_AND_SLEEP() immediately after the main task blocks, but stays awake for the last used timeout time that was unequal to portMAX_DELAY.

When stepping through the code with the debugger I can see that the main task is placed in the overflow list when calling xSemaphoreTake( interval_timer, portMAX_DELAY ). In this case NextTaskUnblockTime is not changed and still holds the timeout time from the last xQueueReceive call from one of the UART drivers. When moving on to portTASK_FUNCTION(), the tickless idle feature checks xExpectedIdleTime
which is equal to xNextTaskUnblockTime and still holds the previous timeout time instead of portMAX_DELAY. Once the system was idle for xNextTaskUnblockTime, its value gets updated and the system finally enters sleep mode.

To me it seems to be a problem with blocking on portMAX_DELAY and xNextTaskUnblockTime not getting updated in that case. Is there something I’m missing?

As workaround I could set configEXPECTED_IDLE_TIME_BEFORE_SLEEP lower than my last timeout time or insert a vTaskDelay(0) before my infinite block to force-update xNextTaskUnblockTime, but those aren’t solutions I’m keen to use.

Thanks, Chris

rtel wrote on Monday, November 03, 2014:

Interesting - I will have to look into this scenario. It is understandable that the main task gets placed into the overflow list if you are using a max delay, at that point the idea would be (assuming no other tasks have shorter delays) for the next wake time to be at the time the in-use and overflow lists get switched.

Regards.

rtel wrote on Friday, November 07, 2014:

So looking at this…xNextTaskUnblockTime is updated when it is either reached (at which point it is reset to the next value), or when it is moved closer in time by a task blocking with a block period that is shorted than the current xNextTaskUnblockTime value. If the actual next unblock time is moved back in time xNextTaskUnblockTime is not updated because, when the xNextTaskUnblockTime value is reached, it will automatically get corrected without the integrity of the system being compromised. That is, there will be one wasted call to vTaskSwitchContext(), but the scheduling pattern and timing will still be correct. In the ‘normal’ case that is deemed to be the best solution as the expense of constantly resetting the value each time a task blocks is greater.

So that is fine for most applications, but for low power tickless applications it can have the effect of delaying the time at which the tick is turned off, so depending on the application it might not be fine if entry into sleep mode at the earliest possible time is a key system requirement.

The question then is what to do about it. prvResetNextTaskUnblockTime() could be called from vTaskPlaceOnEventList(), or even prvAddCurrentTaskToDelayedList() [I would have to work through the examples to see which was most appropriate], but if that were done I think it should be guarded by the pre-processor, so the additional calls to prvResetNextTaskUnblockTime() only got compiled in when configUSE_TICKLESS_IDLE was set to 1 - otherwise it will add expense to non-low power applications for no good reason.

Does that match with your own assessment?

Regards.

rtel wrote on Saturday, November 08, 2014:

Having discussed this we are considering adding the following code to the very end of xTaskRemoveFromEventList(). It is not tested yet. Please comment on this with respect to your application:


#if( configUSE_TICKLESS_IDLE == 1 )
{
  /* If a task is blocked on a kernel object then 
  xNextTaskUnblockTime might be set to the blocked 
  task's time out time.  If the task is unblocked for 
  a reason other than a timeout xNextTaskUnblockTime is
  normally left unchanged, because it will automatically 
  get reset to a new value when the tick count equals   
  xNextTaskUnblockTime.  However if tickless idling is 
  used it might be more important to enter sleep mode
  at the earliest possible time - so reset 
  xNextTaskUnblockTime here to ensure it is updated at 
  the earliest possible time. */
  prvResetNextTaskUnblockTime();
}
#endif


christophneukom wrote on Tuesday, November 11, 2014:

I’ve just tested my device with the modification and compared it to the old revision. It seems to solve the problem, the device now goes to sleep immediately after the last task is blocked on max delay.

Amazing support, thanks a lot!