Deadlock prevention patterns in FreeRTOS - Time & Space Complexity
When using FreeRTOS, preventing deadlocks is key to keeping tasks running smoothly.
We want to see how the time cost changes when applying deadlock prevention patterns.
Analyze the time complexity of this deadlock prevention pattern using resource ordering.
// Task tries to take two mutexes in order
if(xSemaphoreTake(mutexA, portMAX_DELAY) == pdTRUE) {
if(xSemaphoreTake(mutexB, portMAX_DELAY) == pdTRUE) {
// Critical section
xSemaphoreGive(mutexB);
}
xSemaphoreGive(mutexA);
}
This code shows a task taking two mutexes in a fixed order to avoid deadlock.
Look for loops or repeated waits that affect time.
- Primary operation: Waiting to take each mutex (xSemaphoreTake calls)
- How many times: Each mutex is taken once per critical section entry
As the number of tasks or mutexes grows, waiting time can increase.
| Input Size (n tasks) | Approx. Operations (wait attempts) |
|---|---|
| 10 | About 10 waits per mutex |
| 100 | About 100 waits per mutex |
| 1000 | About 1000 waits per mutex |
Pattern observation: Waiting grows roughly linearly with the number of tasks competing for mutexes.
Time Complexity: O(n)
This means waiting time grows linearly as more tasks try to access the same resources.
[X] Wrong: "Taking mutexes in any order won't affect waiting time or deadlocks."
[OK] Correct: Taking mutexes in different orders can cause deadlocks, making tasks wait forever, which is worse than linear waiting.
Understanding how deadlock prevention affects waiting time shows you can write safe multitasking code that scales well.
"What if tasks could take mutexes in any order? How would the time complexity and system behavior change?"
Practice
What is a common method to prevent deadlocks in FreeRTOS when multiple tasks need several mutexes?
Choose the best practice.
Solution
Step 1: Understand deadlock cause in FreeRTOS
Deadlocks happen when tasks wait forever for mutexes held by each other in a cycle.Step 2: Apply fixed order mutex acquisition
Taking mutexes always in the same order prevents circular wait, stopping deadlocks.Final Answer:
Always take mutexes in a fixed global order -> Option DQuick Check:
Fixed mutex order = deadlock prevention [OK]
- Taking mutexes in random order
- Ignoring priority inheritance
- Using recursive mutexes incorrectly
Which of the following is the correct way to create a mutex in FreeRTOS to help prevent deadlocks?
?Solution
Step 1: Identify FreeRTOS mutex creation functions
FreeRTOS uses xSemaphoreCreateMutex() to create a standard mutex.Step 2: Differentiate from other semaphore types
Counting semaphores and recursive mutexes serve different purposes; standard mutex is best for deadlock prevention.Final Answer:
xSemaphoreCreateMutex(); -> Option AQuick Check:
Standard mutex creation = xSemaphoreCreateMutex() [OK]
- Confusing counting semaphore with mutex
- Using recursive mutex without need
- Trying to create mutex with queue functions
Consider this FreeRTOS code snippet where two tasks try to take two mutexes in different orders:
Task1:
xSemaphoreTake(mutexA, portMAX_DELAY);
xSemaphoreTake(mutexB, portMAX_DELAY);
Task2:
xSemaphoreTake(mutexB, portMAX_DELAY);
xSemaphoreTake(mutexA, portMAX_DELAY);What is the likely outcome?
Solution
Step 1: Analyze mutex acquisition order
Task1 takes mutexA then mutexB; Task2 takes mutexB then mutexA, creating circular wait.Step 2: Identify deadlock condition
Each task waits for the other's mutex, causing deadlock with portMAX_DELAY blocking indefinitely.Final Answer:
Deadlock occurs because tasks wait on each other's mutex -> Option CQuick Check:
Opposite mutex order + infinite wait = deadlock [OK]
- Assuming tasks run without blocking
- Ignoring portMAX_DELAY effect
- Thinking one task fails immediately
Given this FreeRTOS code snippet, identify the problem that can cause deadlock:
void Task1(void *pvParameters) {
xSemaphoreTake(mutex1, portMAX_DELAY);
vTaskDelay(10);
xSemaphoreTake(mutex2, portMAX_DELAY);
// ...
xSemaphoreGive(mutex2);
xSemaphoreGive(mutex1);
}
void Task2(void *pvParameters) {
xSemaphoreTake(mutex2, portMAX_DELAY);
vTaskDelay(10);
xSemaphoreTake(mutex1, portMAX_DELAY);
// ...
xSemaphoreGive(mutex1);
xSemaphoreGive(mutex2);
}What is the best fix to prevent deadlock?
Solution
Step 1: Identify cause of deadlock
Task1 takes mutex1 then mutex2; Task2 takes mutex2 then mutex1, causing circular wait and deadlock.Step 2: Apply fixed mutex order to both tasks
Changing Task2 to take mutex1 then mutex2 avoids circular wait and prevents deadlock.Final Answer:
Make both tasks take mutexes in the same order -> Option BQuick Check:
Same mutex order = no deadlock [OK]
- Removing delays does not fix deadlock
- Using recursive mutexes won't solve order problem
- Increasing delay time only delays deadlock
You have three tasks that need to access three shared resources protected by mutexes: mutexX, mutexY, and mutexZ. To prevent deadlocks, which strategy is best?
- Task A needs
mutexXandmutexY - Task B needs
mutexYandmutexZ - Task C needs
mutexXandmutexZ
Choose the best deadlock prevention pattern.
Solution
Step 1: Analyze mutex requirements and potential deadlocks
Tasks share mutexes in overlapping pairs, risking circular wait if order differs.Step 2: Apply fixed global order for mutex acquisition
Taking mutexes always in alphabetical order (mutexX, mutexY, mutexZ) prevents circular wait and deadlocks.Final Answer:
Each task takes mutexes in alphabetical order: mutexX, mutexY, mutexZ -> Option AQuick Check:
Global fixed mutex order = deadlock prevention [OK]
- Taking mutexes randomly causes deadlocks
- Recursive mutexes don't fix order issues
- Waiting for all mutexes simultaneously is complex
Ready to prove it?
Pass the test and this pattern moves unknown
