Bird
Raised Fist0
FreeRTOSprogramming~7 mins

Deadlock prevention patterns in FreeRTOS

Choose your learning style10 modes available
Introduction

Deadlocks stop programs from running by making tasks wait forever. Preventing deadlocks helps keep your FreeRTOS system running smoothly and reliably.

When multiple tasks need to access shared resources like sensors or communication buses.
When tasks use multiple mutexes or semaphores and could block each other.
When designing real-time systems where delays can cause failures.
When debugging a system that sometimes freezes or stops responding.
When you want to improve system stability and avoid unexpected hangs.
Syntax
FreeRTOS
/* Example pattern: Always take mutexes in the same order */
if (xSemaphoreTake(mutex1, portMAX_DELAY) == pdTRUE) {
    if (xSemaphoreTake(mutex2, portMAX_DELAY) == pdTRUE) {
        // Access shared resources safely
        xSemaphoreGive(mutex2);
    }
    xSemaphoreGive(mutex1);
}

Always acquire multiple mutexes in a fixed order to avoid circular waiting.

Use timeout values to avoid waiting forever on a resource.

Examples
Always take mutexA before mutexB to prevent deadlock.
FreeRTOS
/* Pattern 1: Fixed order mutex acquisition */
xSemaphoreTake(mutexA, portMAX_DELAY);
xSemaphoreTake(mutexB, portMAX_DELAY);
// Use resources
xSemaphoreGive(mutexB);
xSemaphoreGive(mutexA);
Try to take mutexes with timeout to avoid waiting forever.
FreeRTOS
/* Pattern 2: Use try-take with timeout */
if (xSemaphoreTake(mutexA, 100) == pdTRUE) {
    if (xSemaphoreTake(mutexB, 100) == pdTRUE) {
        // Use resources
        xSemaphoreGive(mutexB);
    }
    xSemaphoreGive(mutexA);
} else {
    // Handle timeout, try again later
}
Reducing nested locks lowers deadlock risk.
FreeRTOS
/* Pattern 3: Avoid nested mutexes if possible */
// Design tasks so they only need one mutex at a time
xSemaphoreTake(mutexA, portMAX_DELAY);
// Use resource
xSemaphoreGive(mutexA);
Sample Program

This program creates two tasks that always take mutex1 before mutex2. This fixed order prevents deadlocks. The tasks print messages when they take and give mutexes, showing the order clearly.

FreeRTOS
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include <stdio.h>

SemaphoreHandle_t mutex1;
SemaphoreHandle_t mutex2;

void Task1(void *pvParameters) {
    while(1) {
        if (xSemaphoreTake(mutex1, portMAX_DELAY) == pdTRUE) {
            printf("Task1 took mutex1\n");
            if (xSemaphoreTake(mutex2, portMAX_DELAY) == pdTRUE) {
                printf("Task1 took mutex2\n");
                // Simulate work
                vTaskDelay(pdMS_TO_TICKS(100));
                xSemaphoreGive(mutex2);
                printf("Task1 gave mutex2\n");
            }
            xSemaphoreGive(mutex1);
            printf("Task1 gave mutex1\n");
        }
        vTaskDelay(pdMS_TO_TICKS(200));
    }
}

void Task2(void *pvParameters) {
    while(1) {
        if (xSemaphoreTake(mutex1, portMAX_DELAY) == pdTRUE) {
            printf("Task2 took mutex1\n");
            if (xSemaphoreTake(mutex2, portMAX_DELAY) == pdTRUE) {
                printf("Task2 took mutex2\n");
                // Simulate work
                vTaskDelay(pdMS_TO_TICKS(50));
                xSemaphoreGive(mutex2);
                printf("Task2 gave mutex2\n");
            }
            xSemaphoreGive(mutex1);
            printf("Task2 gave mutex1\n");
        }
        vTaskDelay(pdMS_TO_TICKS(150));
    }
}

int main(void) {
    mutex1 = xSemaphoreCreateMutex();
    mutex2 = xSemaphoreCreateMutex();

    if (mutex1 == NULL || mutex2 == NULL) {
        printf("Failed to create mutexes\n");
        return 1;
    }

    xTaskCreate(Task1, "Task1", 1000, NULL, 1, NULL);
    xTaskCreate(Task2, "Task2", 1000, NULL, 1, NULL);

    vTaskStartScheduler();

    // Should never reach here
    return 0;
}
OutputSuccess
Important Notes

Always design your system so tasks acquire mutexes in the same order to prevent circular waiting.

Use timeouts or try-take functions to avoid waiting forever on a resource.

Reducing the number of nested mutexes lowers the chance of deadlocks.

Summary

Deadlock prevention keeps your FreeRTOS tasks running without freezing.

Always take multiple mutexes in a fixed order to avoid circular waits.

Use timeouts and avoid nested locks when possible.

Practice

(1/5)
1.

What is a common method to prevent deadlocks in FreeRTOS when multiple tasks need several mutexes?

Choose the best practice.

easy
A. Avoid using mutexes altogether
B. Allow tasks to take mutexes in any order
C. Use recursive mutexes without order
D. Always take mutexes in a fixed global order

Solution

  1. Step 1: Understand deadlock cause in FreeRTOS

    Deadlocks happen when tasks wait forever for mutexes held by each other in a cycle.
  2. Step 2: Apply fixed order mutex acquisition

    Taking mutexes always in the same order prevents circular wait, stopping deadlocks.
  3. Final Answer:

    Always take mutexes in a fixed global order -> Option D
  4. Quick Check:

    Fixed mutex order = deadlock prevention [OK]
Hint: Take mutexes always in the same order to avoid deadlocks [OK]
Common Mistakes:
  • Taking mutexes in random order
  • Ignoring priority inheritance
  • Using recursive mutexes incorrectly
2.

Which of the following is the correct way to create a mutex in FreeRTOS to help prevent deadlocks?

?
easy
A. xSemaphoreCreateMutex();
B. xSemaphoreCreateCounting(1, 1);
C. xQueueCreateMutex();
D. xSemaphoreCreateRecursiveMutex();

Solution

  1. Step 1: Identify FreeRTOS mutex creation functions

    FreeRTOS uses xSemaphoreCreateMutex() to create a standard mutex.
  2. Step 2: Differentiate from other semaphore types

    Counting semaphores and recursive mutexes serve different purposes; standard mutex is best for deadlock prevention.
  3. Final Answer:

    xSemaphoreCreateMutex(); -> Option A
  4. Quick Check:

    Standard mutex creation = xSemaphoreCreateMutex() [OK]
Hint: Use xSemaphoreCreateMutex() for standard mutex creation [OK]
Common Mistakes:
  • Confusing counting semaphore with mutex
  • Using recursive mutex without need
  • Trying to create mutex with queue functions
3.

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?

medium
A. One task always fails to take mutex
B. Both tasks run without blocking
C. Deadlock occurs because tasks wait on each other's mutex
D. Tasks release mutexes immediately, no deadlock

Solution

  1. Step 1: Analyze mutex acquisition order

    Task1 takes mutexA then mutexB; Task2 takes mutexB then mutexA, creating circular wait.
  2. Step 2: Identify deadlock condition

    Each task waits for the other's mutex, causing deadlock with portMAX_DELAY blocking indefinitely.
  3. Final Answer:

    Deadlock occurs because tasks wait on each other's mutex -> Option C
  4. Quick Check:

    Opposite mutex order + infinite wait = deadlock [OK]
Hint: Opposite mutex order with infinite wait causes deadlock [OK]
Common Mistakes:
  • Assuming tasks run without blocking
  • Ignoring portMAX_DELAY effect
  • Thinking one task fails immediately
4.

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?

medium
A. Remove all delays between mutex takes
B. Make both tasks take mutexes in the same order
C. Use recursive mutexes instead
D. Increase delay time to avoid overlap

Solution

  1. Step 1: Identify cause of deadlock

    Task1 takes mutex1 then mutex2; Task2 takes mutex2 then mutex1, causing circular wait and deadlock.
  2. Step 2: Apply fixed mutex order to both tasks

    Changing Task2 to take mutex1 then mutex2 avoids circular wait and prevents deadlock.
  3. Final Answer:

    Make both tasks take mutexes in the same order -> Option B
  4. Quick Check:

    Same mutex order = no deadlock [OK]
Hint: Fix mutex order in all tasks to the same sequence [OK]
Common Mistakes:
  • Removing delays does not fix deadlock
  • Using recursive mutexes won't solve order problem
  • Increasing delay time only delays deadlock
5.

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?

  1. Task A needs mutexX and mutexY
  2. Task B needs mutexY and mutexZ
  3. Task C needs mutexX and mutexZ

Choose the best deadlock prevention pattern.

hard
A. Each task takes mutexes in alphabetical order: mutexX, mutexY, mutexZ
B. Each task takes mutexes in random order to improve speed
C. Use recursive mutexes for all mutexes
D. Tasks take mutexes only when all are free simultaneously

Solution

  1. Step 1: Analyze mutex requirements and potential deadlocks

    Tasks share mutexes in overlapping pairs, risking circular wait if order differs.
  2. Step 2: Apply fixed global order for mutex acquisition

    Taking mutexes always in alphabetical order (mutexX, mutexY, mutexZ) prevents circular wait and deadlocks.
  3. Final Answer:

    Each task takes mutexes in alphabetical order: mutexX, mutexY, mutexZ -> Option A
  4. Quick Check:

    Global fixed mutex order = deadlock prevention [OK]
Hint: Use global fixed order for all mutex acquisitions [OK]
Common Mistakes:
  • Taking mutexes randomly causes deadlocks
  • Recursive mutexes don't fix order issues
  • Waiting for all mutexes simultaneously is complex
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard