DeadLock occurs when multiple tasks are waiting for each other to unlock the shared resources, resulting in a system blocked.

In this example, we going to create an application to cause a deadlock, to observe and analyze the behavior of Mutex and tasks.

Code Example:

#include "bsp.h"

SemaphoreHandle_t  xMutex1;  /* Mutex semaphore 1 */
SemaphoreHandle_t  xMutex2;  /* Mutex semaphore 2 */ 

static void Task1( void *pvParameters );   /* Task1 To turn on LEDs */
static void Task2( void *pvParameters );   /* Task2 to turn off LEDs */

int main( void )
{
    HAL_Init();

    SEGGER_SYSVIEW_Conf( );
    SEGGER_SYSVIEW_Start( );

    xMutex1 = xSemaphoreCreateMutex();       /* Create the first Mutex Semaphore */
    xMutex2 = xSemaphoreCreateMutex();       /* Create the second Mutex Semaphore*/
    
    xTaskCreate( Task1, "Task1", 240, NULL, 2, NULL );  /* Register Task1 to turn on LEDs */
    xTaskCreate( Task2, "Task2", 240, NULL, 1, NULL );  /* Register Task2 to turn off LEDs */
    
    vTaskStartScheduler();                  /* Init the Kernel */
}

void Task1( void *pvParameters )
{
    for(;;)
    {
        xSemaphoreTake( xMutex1, portMAX_DELAY );                               /* Take the mutex 1 */
        SEGGER_SYSVIEW_PrintfHost( "Task 1 takes the Mutex 1" );
        vTaskDelay(250);
        SEGGER_SYSVIEW_PrintfHost( "Task 1 trying to take Resource 2" );
        xSemaphoreTake( xMutex2, portMAX_DELAY );                               /* Try to get mutex 2 */
        vTaskDelay(250);

        /* At this point the Deadlock occurs */

        SEGGER_SYSVIEW_PrintfHost("Task 1 gives the resource 2");            
        xSemaphoreGive( xMutex2 );                                              /* give the mutex 2 */
        SEGGER_SYSVIEW_PrintfHost("Task 1 gives the resource 1");            
        xSemaphoreGive( xMutex1 );                                              /* give the mutex 1 */
    }
}

void Task2( void *pvParameters )
{
    for(;;)
    {
        xSemaphoreTake( xMutex2, portMAX_DELAY );                               /* Take the mutex 2 */
        SEGGER_SYSVIEW_PrintfHost( "Task 2 takes the Mutex 2" );
        vTaskDelay(250);
        SEGGER_SYSVIEW_PrintfHost( "Task 2 trying to take Resource 1" );
        xSemaphoreTake( xMutex1, portMAX_DELAY );                              /* Try to get the mutex 1 */
        vTaskDelay(250);

        /* At this point the Deadlock occurs */

        SEGGER_SYSVIEW_PrintfHost("Task 2 gives the resource 1");
        xSemaphoreGive( xMutex1 );                                             /* Gives the mutex 1 */
        SEGGER_SYSVIEW_PrintfHost("Task 2 gives the resource 2");
        xSemaphoreGive( xMutex2 );                                             /* Gives the mutex 2 */ 
    }   
}

The application manages 2 mutexes controlled by 2 tasks. Each task will control both mutex to cause a DeadLock in the system. Observe the code of the 2 task, Task1 going to execute and takes the xMutex1, Task2 is created similar, this task must take the xMutex2. when both tasks are ready to execute again, it's going to try to get the other mutex, which means, Task1 will try to take the xMutex2 and Task2 will try to get the xMutex1 but if you notice these mutexes are not available so the tasks enter in a block state waiting to the other task release the mutex taked first.

SystemView Output:

Observe the terminal, Tasks are executed taking each one its mutex, when the task executes again the Mutex to get is not available and the task blocks, waiting for the other task to give the mutex take.

Document

TimeLine:

Document

At the init, Tasks are executed. Notice that first Task 1 is executed taking the mutex 1, after, Task 2 is executed to take the mutex 2.
Tasks are now in block state until the tick delay is complete.

When the tasks are executed. observe that Task1 get blocked because is waiting to the mutex 2 taked before by Task 2, so the task changes the context.
When Task 2 is executed occurs the same, Task 2 now is blocked waiting the mutex 1 taked before by Task 1.