Consider three tasks with priorities: High (3), Medium (2), and Low (1). The Low priority task holds a mutex needed by the High priority task. The Medium priority task runs continuously without blocking. What happens to the High priority task?
/* Pseudocode for FreeRTOS tasks */ void LowPriorityTask(void *pvParameters) { xSemaphoreTake(mutex, portMAX_DELAY); // Simulate long processing vTaskDelay(pdMS_TO_TICKS(1000)); xSemaphoreGive(mutex); vTaskDelete(NULL); } void MediumPriorityTask(void *pvParameters) { while(1) { // Runs continuously without blocking } } void HighPriorityTask(void *pvParameters) { xSemaphoreTake(mutex, portMAX_DELAY); // Critical section xSemaphoreGive(mutex); vTaskDelete(NULL); }
Think about how Medium priority task running continuously affects Low priority task holding the mutex.
The Medium priority task, running continuously, prevents the Low priority task from running and releasing the mutex. This causes the High priority task to be blocked indefinitely waiting for the mutex, a classic priority inversion scenario.
FreeRTOS provides a feature to avoid priority inversion when a low priority task holds a resource needed by a higher priority task. What is this feature called?
It temporarily raises the priority of the task holding the resource.
Priority inheritance temporarily raises the priority of the low priority task holding the resource to the priority of the highest waiting task, preventing priority inversion.
Given the following FreeRTOS task setup, why does the Low priority task never get CPU time?
xTaskCreate(HighPriorityTask, "High", 1000, NULL, 3, NULL); xTaskCreate(MediumPriorityTask, "Medium", 1000, NULL, 2, NULL); xTaskCreate(LowPriorityTask, "Low", 1000, NULL, 1, NULL); void MediumPriorityTask(void *pvParameters) { while(1) { // Busy loop without any delay or blocking } } void LowPriorityTask(void *pvParameters) { while(1) { // Should run but never does vTaskDelay(pdMS_TO_TICKS(100)); } }
Look at the Medium priority task's loop behavior.
The Medium priority task runs an infinite busy loop without any delay or blocking call, so it never yields CPU time. This causes the Low priority task to starve and never run.
Consider a Low priority task holding a mutex and a High priority task waiting for it. Priority inheritance is enabled. What priority does the Low priority task temporarily get?
/* Assume priorities: Low=1, Medium=2, High=3 */ // Low priority task takes mutex xSemaphoreTake(mutex, portMAX_DELAY); // High priority task tries to take mutex and blocks xSemaphoreTake(mutex, portMAX_DELAY); // Priority inheritance raises Low priority task's priority // What is Low priority task's temporary priority?
Priority inheritance raises the priority to the highest waiting task's priority.
Priority inheritance raises the priority of the Low priority task holding the mutex to the priority of the highest priority task waiting for the mutex, which is 3.
You have three tasks: High, Medium, and Low priority. The Low priority task holds a mutex needed by the High priority task. The Medium priority task runs continuously and starves the Low priority task. Which approach fixes the priority inversion and starvation?
Think about both priority inversion and starvation causes.
Enabling priority inheritance prevents priority inversion by boosting Low priority task's priority temporarily. Adding blocking calls in Medium priority task allows Low priority task to run and release the mutex, fixing starvation.