While working with FreeRTOS V10.3.1, I encountered some behaviors and would like to understand more about how the scheduler operates when pre-emptive scheduling without time slicing is configured.
Task Information:
Task_A : Priority 3, delay 1 ms
Task_B1: Priority 2, delay 1 ms
Task_B2: Priority 2, delay 10 ms
Scheduler Configuration:
Pre-emptive Scheduling without Time Slicing
configUSE_PREEMPTION = 1
configUSE_TIME_SLICING = 0
Questions:
If Task_B1 and Task_B2 are already in the Ready state and Task_A enters the Blocked state, which task will the scheduler choose to run ?
If Task_B1 is in the Running state and Task_B2 is in the Ready state, and Task_B1 gets pre-empted by Task_A, which task should resume running once Task_A enters the Blocked state?
Is there an API to limit the execution time of the highest-priority task (Task_A)? Sometimes Task_A runs for too long, and I want to limit its runtime to allocate more time for lower-priority tasks (Task_B1, Task_B2). I am not sure taskYIELD or vTaskSetTimeOutState/xTaskCheckForTimeOut can help to in this case.
Another concern is related to run-time statistics. I assume that the total percentage should sum up to 100% when using 'vTaskGetRunTimeStats".
If Task_B1 and Task_B2 are already in the Ready state and Task_A enters the Blocked state, which task will the scheduler choose to run ?
Which ever has been in the ready state the longest.
If Task_B1 is in the Running state and Task_B2 is in the Ready state, and Task_B1 gets pre-empted by Task_A, which task should resume running once Task_A enters the Blocked state?
If both B tasks are in the ready state, then whichever has been waiting in the ready state the longest will get selected, so B2 in this case.
Is there an API to limit the execution time of the highest-priority task (Task_A)? Sometimes Task_A runs for too long, and I want to limit its runtime to allocate more time for lower-priority tasks (Task_B1, Task_B2). I am not sure taskYIELD or vTaskSetTimeOutState/xTaskCheckForTimeOut
The scheduler will always select the highest priority task that is able to run, so unless Task A changes its own priority, or blocks, it will continue running. taskYIELD() will only result in Task A running again, if it is not in the Blocked or Suspended state. The set and check for timeout functions donât put the task into the Blocked state. You could call vTaskDelay(1), which will put the task into the Blocked state until the next tick interrupt occurs - but then if that is ok is task A really higher priority?
On the runtime stats - the percentage should add up to 100, as far as integer maths can be that accurate, in reality there is integer rounding. The percentage that is >1000 just looks like a bug. Is it possible the run time counter is being read somehow before the scheduler starts and sets it to zero?
I have put a watchdog task into Task_B2 (previously was placed Task_B1) and it results a watchdog reset due to the Task_B2 is in Ready state for a long time. Maybe I have done something wrong.
Is there any function I can use to collect how long a task has been in the Ready state?
Do you think the following snapshot precisely captures the Ready duration?
Is there any callback I can set to handle a task timeout?
vTaskDelay(1) seems promising, but I would like to place it in a callback whenever a timeout occurs. I am considering using a software timer, but I am not sure if it would work in this case.
As to your question on the run-time state issue. It would be helpful to know what port you are using, and what timer it is using for the run time states. That real big number is a reasonable number (about 6%) with the high bit set, so there may have been a glitch in the counter which messed things up.
As to your questions about longest ready time, FreeRTOS doesnât actually look at a âtimeâ value for that, but uses a linked list and when a task gets switched out, it goes to the back of the list at its priority.
The original issue is the Task_B2 is not able to be in Running state for a long time when there is large data being received, categorized, pre-processed by Task_A (highest prio) and post-processed by the Task_B1 (same prio but less being Blocked - less delay).
I have place a watchdog task into Task_B2 to see if the Task_B2 can serve watchdog timer. As a result, watchdog reset occurred.
When there is a large data being received and processed Task_A takes around ~72% and Task_B1 takes around ~21% of execution time.
Where are the files for port.c / portmacro.h etc coming from, that will be some directory in the portable hierarchy (unless maybe you are using a 3rd party port)
The portable directory for Tasking only lists an ARM_CM4F directory, and I am not sure that the Infineon parts are Arm CM4 cores (a quick look at TC2xx parts donât say they are) so I am not sure if that port is suitable for the processor.
If Task_B1 is in the Running state and Task_B2 is in the Ready state, and Task_B1 gets pre-empted by Task_A, which task should resume running once Task_A enters the Blocked state?
Task B_1 will resume execution once Task A enters the Blocked state. A task which is in the âRunningâ state will remain in itâs position as the longest ready task. Here is the code for proof. To reset itâs position in the ready list to the back of the list the task will need to yield or block.
This explains why youâre seeing task B2 starved for execution time.
Your explanation matches my observations. I am going to collect pvOwner overtime to see what task would be in the Running state once Task_A enters the Blocked state.
Yield or Block may work but how do I know where exactly Task_B1 resumes in the Running state or how can I set a timeout for Task_B1 if it stays in the Running state too long.
I was looking into xTimerCreate but I would not the case since taskYIELD() must be called within a Task_B1.
The ideas about timer out is Timer will start at beginning of task and will stop when all jobs have done.
void TimeOutCallback(void)
{
taskYIELD();
}
void Task_B1(void *pvParameters)
{
for(;;;)
{
TimerStart();
// Do something A.
// Do something B.
// Do something C.
TimerStop();
vTaskDelayUntil()
}
}
First thing, âTimeoutâ isnât âRun too longâ, but is about blocking to long for something. If you want to detect running to long, the task needs to keep track of how long it has been running.
Your problem of ârunning too longâ seems to be an indication of not having your priorities set right. If one task âneedsâ to run and another make way for it, then it should have a higher priority.
Iâd suggest maybe taking a look at our book to better understand the impacts of preemptive and time slicing on scheduling. To answer your immediate question:
Task B1 can be preempted by task A at any time - if it is preempted then there is no way to know where exactly where it was in execution at the time of preemption. This means there is no way to predict where it will resume.
Task B1 will resume exactly after its yield/blocking IF it is cooperatively swapped in from Task B2 (aka B2 calls a yield).
As @richard-damon has stated the behavior youâre looking for and the configuration youâve set seem at odds with each other.
Iâd first start by understanding how the FreeRTOS scheduler works with tasks of different priorities and the impacts of preemption and time slicing. A good explanation of this is found in the kernel book mentioned in my previous comment.
Many thanks, I truly appreciate your time in helping me understand the functionality of FreeRTOS. Increasing the priority of Task_B2 has resolved the issue, and it was actually the first workaround I tried before raising my concern. Optimizing Task_A, Task_B2 and distributing workload across other cores seems to be the long-term solution.
I think you must mean something other than what this seems to say. B1 had better resume exactly where it left off!
Iâm not a FreeRTOS expert, but other tasks wonât be able to tell where B1 had got to (you could arrange some sort of job step counter, so they could say âB1 has completed job step xâ.
It is trivially true that preemptive scheduling is transparent to the affected task, so task B1 will never âknowâ that it has been preempted and thus does not need to"know" where to resume.
In my reading it is implied that the inability to predict where it will resume applies to other tasks and most system components.
Of course! Apologies if I wasnât clear. Task B1 will resume exactly where it was preempted. What is was meaning to say is there is no way to predict where preemption will occur in Task B1 without some sort of synchronization mechanism between A and B1.