NextTaskUnblockTime math on sentinal

While looking into why tickless is not working, I came across this:

xNextTaskUnblockTime is assigned a sentinal value if there is not wake time (everything blocking with no timeouts).
prvGetExpectedIdleTime returns (baring something being awake) xNextTimeUnblockTime - xTickCount

If there is nothing waiting for time, this will be the sentinal, which when subtracting now, is no longer the sentinal, so just a large value. This is then passed to SupressTicksAndSleep where some vendor implementations check for the sentinal to sleep forever vs setting a interrupt on the RTC to wake up later. Since the sentinal is never actually used, this does not happen in practice.

Also a side-effect, if the next timeout IS a long way away, and the math results in a value matching the sentinal, those implementations will sleep forever, not waking until a different interrupt happens. This is admittedly a rare case, but it could be an issue.

It seems weird that vendor implementations would look for the sentinel value (which is an unblock time) when the parameter passed to portSUPPRESS_TICKS_AND_SLEEP() is a duration not a time. I would think using the parameter that way isn’t right.

My understanding of this thread is that the issue is not in our code, but code provided by a partner. Please let me know if that understanding is incorrect.

Well, the Duration of portMAX_DELAY is not being sent into SupressTicksAndSleep ever, or at least not as the same meaning of infinite sleeping. And that function takes a duration, so it is reasonable to assume that the statement is true.

Yes the main issue is with the Vendor (ST) code.

Well, portMAX_DELAY is a duration, and so is the parameter to SupressAndSleep(). So reasonable to assume. Also, prvResetNextTaskUnblockTime() initializes xNextTaskUnblockTime to portMAX_DELAY and that is what is set when there is NO expected unblock, so again reasonable to assume. But when it is returned as such by prvGetExpectedIdleTime it is unconditionally pulled back by xTickCount as if it was a time, not ‘never’

However this is complicated by the fact that the next wake may really be at TIME portMAX_DELAY at nothing is protecting that from happening.

Using portMAX_DELAY as a time (not as a duration) is perhaps not ideal from a readability point of view, and should perhaps be something like portMAX_TICK_COUNT but have the same value. An example of that kind of usage is here. But note that as a time (not a duration), this value is an actual time – it doesn’t represent “never” in the same way the duration represents “forever”. The OS actually needs to wake up at that time to be ready for the tick-count rollover and associated maintenance.

Using portMAX_DELAY as a duration does often mean “forever”, but not in the portSUPPRESS_TICKS_AND_SLEEP() interface. As you noted FreeRTOS code may pass portMAX_DELAY to portSUPPRESS_TICKS_AND_SLEEP() and not to mean “forever”.

If the expected idle time actually is forever, the tickless implementation can identify that condition in the return value from eTaskConfirmSleepModeStatus(). Return value eNoTasksWaitingTimeout tells the tickless implementation it can literally stop the underlying timer that provides the tick.

Your point is well taken that it is easy for a vendor to make a wrong assumption in this interface, and I will add that it’s even easier for a vendor to make a mistake implementing this function. Some tricky stuff here.

I see that there is a need to deal with tick rollover, I just was looking into why my tickless was not working and came across this confusing bit of code.

I can pass the feedback to use ConfirmSleepModeStatus instead of trying (and mostly failing) to use portMAX_DELAY to imply it.