rtel wrote on Friday, July 05, 2013:
Well, you could just not create the task until the time that it first needs to run, so you change your code to create the task once then let it control its own timing. The task would never have to enter the Suspended state at all if it didn’t exist until it needed to execute the first time.
However, the ‘issue’ as defined in this thread is only an issue if the task is suspended between calls to vTaskDelayUntil(). I will try and demonstrate below:
void vATask( void *pvParameters )
const portTickType x30SecondsInTicks = 30000 / portTICK_RATE_MS;
// A Initialise the last run time variable.
xLastRunTime = xTaskGetTickCount();
// B Enter a loop to run every 30 seconds.
for( ;; )
// C Wait until it is time to run again.
vTaskDelayUntil( &xLastRunTime, x30SecondsInTicks );
// D Perform processing here.
// E Loop back to 'C' to wait for the next execution time.
Scenario, no suspension, normal execution:
1) The task start to run and takes a snapshot of the time at A.
2) The task blocks at C relative to the time taken at A. The unblock time will be 30 seconds in the future.
3) At D xLastRunTime will equal its initial value + x30SecondsInTicks, everything is ok.
4) At E the processing for this cycle is complete and the task loops back to C, to wait another 30 seconds.
5) The task wakes at the correct time and at D performs its processing.
That scenario can loop as many times as you like with correct behaviour, now lets change the scenario at some point…
6) Between D and E the task is suspended for 40 seconds.
7) At E the task loops back to C, but this time xLastRunTime was 40 seconds ago (plus whatever the time it takes to process the loop), so it asks to wake up 40 seconds ago plus 30 seconds, which is 10 seconds in the past.
8) vTaskDelayUntil() adds 30 seconds worth of ticks to xLastRunTime and returns immediately. Now xLastRunTime is 10 seconds in the past.
9) D and E execute without the task being suspended.
10) vTaskDelayUntil() is called again asking to wake up 30 seconds after a time that is already 10 seconds in the past, so the task enters the Blocked state for 20 seconds only. Now the time has caught up with itself and the task has executed its processing the expected number of times.
Now assume the task is created in the Suspended state (which is possible if the task is created and then suspended before the scheduler is started, or simply by calling TaskSuspend( NULL ) as the first line of the task so it runs then immediately suspends itself - which is ok provided it is guaranteed to happen before anything tries to resume it). It does not matter how long it stays in the suspended state because it has not yet initialised xLastRunTime. When it leaves the Suspended state the first thing it will do is as per the comment line A above - which is to initialise xLastRunTime. Then the scenario as described above executes in exactly the same way using a time relative to the time it left the Suspended state rather than the time the task started running.